Я много читал о замыканиях и думаю, что понимаю их, но, не омрачая картины для себя и других, я надеюсь, что кто-нибудь сможет объяснить замыкания настолько кратко и ясно, насколько это возможно. Я ищу простое объяснение, которое могло бы помочь мне понять, где и почему я хотел бы их использовать.
python
functional-programming
closures
известный гражданин
источник
источник
nonlocal
был добавлен в python 3, в python 2.x не было полноценных закрытий для чтения-записи (то есть вы могли читать закрытые переменные, но не изменять их значения)nonlocal
ключевое слово в Python 2, используя изменяемый объект, например,L = [0] \n def counter(): L[0] += 1; return L[0]
то есть вы не можете изменить имя (привязать его к другому объекту) в этом случае, но вы можете изменить сам изменяемый объект, на который ссылается имя к. Список необходим, потому что целые числа неизменяемы в Python.Это просто: функция, которая ссылается на переменные из содержащей области видимости, потенциально после того, как поток управления покинул эту область. Последний бит очень полезен:
>>> 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 правильными замыканиями.
источник
constant = x
; вы можете просто сделать этоreturn y + x
во вложенной функции (или получить аргумент с именемconstant
), и все будет работать нормально; аргументы захватываются замыканием не иначе, как локальные переменные без аргументов.Мне нравится это грубое, емкое определение :
Я бы добавил
Декораторы, которые принимают параметры, обычно используются для замыканий. Замыкания - это обычный механизм реализации для такого рода «фабрики функций». Я часто использую замыкания в паттерне стратегии. когда стратегия изменяется данными во время выполнения.
В языке, который позволяет определять анонимные блоки - например, Ruby, C # - замыкания могут использоваться для реализации (до какой степени) новых структур управления. Отсутствие анонимных блоков является одним из ограничений закрытий в Python .
источник
Честно говоря, я прекрасно понимаю замыкания, за исключением того, что я никогда не понимал, что именно является «закрытием» и что в нем такого «замыкания». Я рекомендую вам отказаться от поиска какой-либо логики в выборе термина.
Во всяком случае, вот мое объяснение:
def foo(): x = 3 def bar(): print x x = 5 return bar bar = foo() bar() # print 5
Ключевой идеей здесь является то, что объект функции, возвращаемый из foo, сохраняет привязку к локальной переменной 'x', даже если 'x' вышел за пределы области видимости и должен быть более не функционирующим. Этот хук относится к самой переменной var, а не только к ее значению в то время, поэтому, когда вызывается bar, он печатает 5, а не 3.
Также имейте в виду, что Python 2.x имеет ограниченное закрытие: я не могу изменить 'x' внутри 'bar', потому что запись 'x = bla' объявляет локальный 'x' в bar, а не назначает 'x' из foo . Это побочный эффект объявления Python assignment =. Чтобы обойти это, 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
источник
Я никогда не слышал, чтобы транзакции использовались в том же контексте, что и объяснение того, что такое закрытие, и здесь действительно нет никакой семантики транзакции.
Это называется закрытием, потому что оно «закрывает» внешнюю переменную (константу), то есть это не просто функция, а оболочка среды, в которой функция была создана.
В следующем примере вызов закрытия 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
источник
g()
вычисляет,x * 2
но ничего не возвращает. Так и должно бытьreturn x * 2
. +1 все же за объяснение слова «закрытие».Вот типичный вариант использования замыканий - обратные вызовы для элементов графического интерфейса (это было бы альтернативой подклассу класса кнопки). Например, вы можете создать функцию, которая будет вызываться в ответ на нажатие кнопки и «закрывать» соответствующие переменные в родительской области, которые необходимы для обработки щелчка. Таким образом, вы можете связать довольно сложные интерфейсы с одной и той же функцией инициализации, встроив все зависимости в замыкание.
источник
В Python замыкание - это экземпляр функции, с которой постоянно связаны переменные.
Фактически, модель данных объясняет это в своем описании
__closure__
атрибута функций :Чтобы продемонстрировать это:
def enclosure(foo): def closure(bar): print(foo, bar) return closure closure_instance = enclosure('foo')
Очевидно, мы знаем, что теперь у нас есть функция, на которую указывает имя переменной
closure_instance
. Якобы, если мы вызываем его с помощью объекта,bar
он должен напечатать строку,'foo'
и что бы там ни было строковое представлениеbar
.На самом деле, строка «Foo» будет связан с экземпляром функции, и мы можем сразу прочитать его здесь, путем доступа к
cell_contents
атрибуту (только и) первой ячейки в кортеже__closure__
атрибута:>>> closure_instance.__closure__[0].cell_contents 'foo'
Кроме того, объекты ячеек описаны в документации C API:
И мы можем продемонстрировать использование нашего закрытия, отметив, что
'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
Есть и более сложные замыкания, которые не подходят для примера частичной функции, и я продемонстрирую их дальше, когда позволит время.
источник
# 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
источник
Вот пример закрытия 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
источник
Для меня «замыкания» - это функции, которые способны запоминать среду, в которой они были созданы. Эта функциональность позволяет вам использовать переменные или методы внутри замыкания, которые иначе вы не сможете использовать, потому что они больше не существуют или недоступны из-за области действия. Посмотрим на этот код на рубине:
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"
он работает даже тогда, когда и метод «умножения», и переменная «x» больше не существуют. Все из-за возможности запоминания закрытия.
источник
все мы использовали декораторы в Python. Это хорошие примеры, показывающие, что такое закрывающие функции в Python.
class Test(): def decorator(func): def wrapper(*args): b = args[1] + 5 return func(b) return wrapper @decorator def foo(val): print val + 2 obj = Test() obj.foo(5)
здесь окончательное значение 12
Здесь функция-оболочка может получить доступ к объекту func, поскольку оболочка является «лексическим замыканием», она может получить доступ к его родительским атрибутам. Вот почему он может получить доступ к объекту func.
источник
Я хотел бы поделиться своим примером и пояснением о закрытии. Я сделал пример Python и два рисунка для демонстрации состояний стека.
def maker(a, b, n): margin_top = 2 padding = 4 def message(msg): print('\n’ * margin_top, a * n, ' ‘ * padding, msg, ' ‘ * padding, b * n) return message f = maker('*', '#', 5) g = maker('', '♥’, 3) … f('hello') g(‘good bye!')
Результат этого кода будет следующим:
***** hello ##### good bye! ♥♥♥
Вот два рисунка, показывающие стеки и замыкание, прикрепленное к объекту функции.
когда функция возвращается от производителя
когда функция вызывается позже
Когда функция вызывается через параметр или нелокальную переменную, коду требуются привязки локальных переменных, такие как margin_top, padding, а также a, b, n. Чтобы код функции работал, должен быть доступен стековый фрейм функции-создателя, которая давно ушла, резервная копия которой содержится в замыкании, которое мы можем найти вместе с объектом функции сообщения.
источник
Лучшее объяснение замыкания, которое я когда-либо видел, - это объяснение механизма. Это было примерно так:
Представьте свой программный стек как вырожденное дерево, в котором каждый узел имеет только одного дочернего элемента, а единственный листовой узел является контекстом вашей выполняемой в настоящее время процедуры.
Теперь ослабьте ограничение, согласно которому каждый узел может иметь только одного дочернего элемента.
Если вы сделаете это, у вас может быть конструкция ('yield'), которая может возвращаться из процедуры, не отбрасывая локальный контекст (т.е. она не выталкивает его из стека при возврате). В следующий раз, когда процедура вызывается, вызов берет старый стек (дерево) фрейм и продолжает выполнение с того места, где он остановился.
источник