Подтвердить что ты не робот

Можете ли вы объяснить закрытие (как они относятся к Python)?

Я много читал о закрытиях, и я думаю, что понимаю их, но, не помутняя картину для себя и других, я надеюсь, что кто-то сможет объяснить закрытие как можно лаконично и ясно. Я ищу простое объяснение, которое могло бы помочь мне понять, где и почему я хотел бы использовать их.

4b9b3361

Ответ 1

Закрытие закрытий

Объекты - это данные с методами прикрепленные, замыкания - это функции с данные прилагаются.

def make_counter():
    i = 0
    def counter(): # counter() is a closure
        nonlocal i
        i += 1
        return i
    return counter

c1 = make_counter()
c2 = make_counter()

print (c1(), c1(), c2(), c2())
# -> 1 2 1 2

Ответ 2

Это просто: функция, которая ссылается на переменные из содержащейся области, потенциально после потока управления, оставила эту область. Этот последний бит очень полезен:

>>> def makeConstantAdder(x):
...     constant = x
...     def adder(y):
...         return y + constant
...     return adder
... 
>>> f = makeConstantAdder(12)
>>> f(3)
15
>>> g = makeConstantAdder(4)
>>> g(3)
7

Обратите внимание, что 12 и 4 "исчезли" внутри f и g, соответственно, эта функция - это то, что делает правильные затворы f и g.

Ответ 3

Мне нравится это грубое, сжатое определение:

Функция, которая может ссылаться на среды, которые больше неактивны.

Я бы добавил

Закрытие позволяет связывать переменные в функцию, не передавая их в качестве параметров.

Декораторы, которые принимают параметры, являются общими для закрытий. Закрытие - это общий механизм реализации такого рода "функция factory". Я часто предпочитаю использовать закрытие в Strategy Pattern, когда стратегия изменяется данными во время выполнения.

В языке, который разрешает анонимное определение блока - например, Ruby, С# - замыкания могут использоваться для реализации (какой суммы) новых новых структур управления. Отсутствие анонимных блоков относится к ограничениям закрытия в Python.

Ответ 4

Честно говоря, я прекрасно понимаю закрытие, за исключением того, что я никогда не понимал, что такое "закрытие" и что такое "закрытие". Я рекомендую вам отказаться от поиска любой логики выбора термина.

В любом случае, здесь мое объяснение:

def foo():
   x = 3
   def bar():
      print x
   x = 5
   return bar

bar = foo()
bar()   # print 5

Ключевой идеей здесь является то, что объект функции, возвращенный из foo, удерживает крючок в локальном var 'x', хотя "x" вышел из сферы действия и должен быть потерян. Этот крючок относится к самому var, а не только к значению, которое var имел в то время, поэтому при вызове bar он печатает 5, а не 3.

Также ясно, что Python 2.x имеет ограниченное закрытие: я не могу изменить "x" внутри "bar", потому что запись "x = bla" объявит локальный "x" в строке, а не присваивает "x" 'foo. Это побочный эффект присваивания Python = объявления. Чтобы обойти это, Python 3.0 представляет нелокальное ключевое слово:

def foo():
   x = 3
   def bar():
      print x
   def ack():
      nonlocal x
      x = 7
   x = 5
   return (bar, ack)

bar, ack = foo()
ack()   # modify x of the call to foo
bar()   # print 7

Ответ 5

Я никогда не слышал о том, что транзакции используются в том же контексте, что и объяснение того, что такое закрытие, и здесь действительно нет никакой семантики транзакций.

Он называется замыканием, потому что он "закрывает" внешнюю переменную (константу) - то есть это не просто функция, а оболочка среды, в которой была создана функция.

В следующем примере вызов замыкания g после смены x также изменит значение x внутри g, так как g закрывается над x:

x = 0

def f():
    def g(): 
        return x * 2
    return g


closure = f()
print(closure()) # 0
x = 2
print(closure()) # 4

Ответ 6

Здесь типичный пример использования для замыканий - обратные вызовы для элементов GUI (это было бы альтернативой подклассу класса кнопок). Например, вы можете построить функцию, которая будет вызываться в ответ на нажатие кнопки и "закрыть" над соответствующими переменными в родительской области, которые необходимы для обработки щелчка. Таким образом, вы можете подключить довольно сложные интерфейсы от одной и той же функции инициализации, построив все зависимости в закрытии.

Ответ 7

В Python замыкание является экземпляром функции, которая имеет неизменные переменные, связанные с ней.

Фактически, модель данных объясняет это в описании атрибута __closure__:

Нет или кортежей ячеек, которые содержат привязки для функций свободных переменных. Только для чтения

Чтобы продемонстрировать это:

def enclosure(foo):
    def closure(bar):
        print(foo, bar)
    return closure

closure_instance = enclosure('foo')

Ясно, что мы знаем, что теперь у нас есть функция, на которую указана переменная name closure_instance. Якобы, если мы назовем его объектом, bar, он должен напечатать строку, 'foo' и любое строковое представление bar.

Фактически, строка 'foo' привязана к экземпляру функции, и мы можем прямо ее прочитать здесь, обратившись к атрибуту cell_contents первой (и единственной) ячейки в кортеже __closure__:

>>> closure_instance.__closure__[0].cell_contents
'foo'

В качестве сторонних объектов ячейки описаны в документации API C:

Объекты "Cell" используются для реализации переменных, на которые ссылаются множественные прицелы

И мы можем продемонстрировать наше использование закрытия, отметив, что 'foo' застревает в функции и не изменяется:

>>> closure_instance('bar')
foo bar
>>> closure_instance('baz')
foo baz
>>> closure_instance('quux')
foo quux

И ничто не может его изменить:

>>> closure_instance.__closure__ = None
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: readonly attribute

Частичные функции

Приведенный пример использует закрытие в качестве частичной функции, но если это наша единственная цель, та же цель может быть достигнута с помощью functools.partial

>>> from __future__ import print_function # use this if you're in Python 2.
>>> partial_function = functools.partial(print, 'foo')
>>> partial_function('bar')
foo bar
>>> partial_function('baz')
foo baz
>>> partial_function('quux')
foo quux

Существуют и более сложные замыкания, которые не подходят для примера частичной функции, и я продемонстрирую их дальше по мере возможности.

Ответ 8

Вот пример закрытия Python3

def closure(x):
    def counter():
        nonlocal x
        x += 1
        return x
    return counter;

counter1 = closure(100);
counter2 = closure(200);

print("i from closure 1 " + str(counter1()))
print("i from closure 1 " + str(counter1()))
print("i from closure 2 " + str(counter2()))
print("i from closure 1 " + str(counter1()))
print("i from closure 1 " + str(counter1()))
print("i from closure 1 " + str(counter1()))
print("i from closure 2 " + str(counter2()))

# result

i from closure 1 101
i from closure 1 102
i from closure 2 201
i from closure 1 103
i from closure 1 104
i from closure 1 105
i from closure 2 202

Ответ 9

Для меня "закрытие" - это функции, которые способны запоминать среду, в которой они были созданы. Эта функциональность позволяет использовать переменные или методы в закрытии, иначе вы не сможете использовать либо потому, что они больше не существуют, либо недоступны из-за объема. Давайте посмотрим на этот код в ruby:

def makefunction (x)
  def multiply (a,b)
    puts a*b
  end
  return lambda {|n| multiply(n,x)} # => returning a closure
end

func = makefunction(2) # => we capture the closure
func.call(6)    # => Result equal "12"  

он работает даже тогда, когда оба метода "multiply" и "x" больше не существуют. Все из-за возможности закрытия памяти.

Ответ 10

# A Closure is a function object that remembers values in enclosing scopes even if they are not present in memory.

# Defining a closure

# This is an outer function.
def outer_function(message):
    # This is an inner nested function.
    def inner_function():
        print(message)
    return inner_function

# Now lets call the outer function and return value bound to name 'temp'
temp = outer_function("Hello")
# On calling temp, 'message' will be still be remembered although we had finished executing outer_function()
temp()
# Technique by which some data('message') that remembers values in enclosing scopes 
# even if they are not present in memory is called closures

# Output: Hello

Критерии для выполнения Закрытий:

  • У нас должна быть вложенная функция.
  • Вложенная функция должна ссылаться на значение, определенное в прилагаемой функции.
  • Функция закрытия должна возвращать вложенную функцию.

# Example 2
def make_multiplier_of(n): # Outer function
    def multiplier(x): # Inner nested function
        return x * n
    return multiplier
# Multiplier of 3
times3 = make_multiplier_of(3)
# Multiplier of 5
times5 = make_multiplier_of(5)
print(times5(3)) # 15
print(times3(2)) #  6

Ответ 11

Лучшее объяснение, которое я когда-либо видел, заключалось в том, чтобы объяснить механизм. Это произошло примерно так:

Представьте свой стек программ как вырожденное дерево, где каждый node имеет только один дочерний элемент, а единственный лист node - это контекст вашей текущей выполняемой процедуры.

Теперь расслабьте ограничение, что каждый node может иметь только один ребенок.

Если вы это сделаете, вы можете получить конструкцию ('yield'), которая может вернуться из процедуры, не отбрасывая локальный контекст (т.е. он не выталкивает ее из стека при возврате). В следующий раз, когда вызывается процедура, вызов захватывает старый стек (дерево) и продолжает выполнение, где он остановился.