Tenho lido muito sobre encerramentos e acho que os entendo, mas sem obscurecer o quadro para mim e para os outros, espero que alguém possa explicar os encerramentos da forma mais sucinta e clara possível. Estou procurando uma explicação simples que possa me ajudar a entender onde e por que eu gostaria de usá-los.
python
functional-programming
closures
cidadão conhecido
fonte
fonte
nonlocal
foi adicionado em python 3, python 2.x não tinha fechamentos completos de leitura e gravação (ou seja, você podia ler variáveis fechadas, mas não alterar seus valores)nonlocal
palavra-chave no Python 2 usando um objeto mutável, por exemplo,L = [0] \n def counter(): L[0] += 1; return L[0]
você não pode alterar o nome (vinculá-lo a outro objeto) neste caso, mas pode alterar o próprio objeto mutável ao qual o nome se refere para. A lista é necessária porque os inteiros são imutáveis em Python.É simples: uma função que faz referência a variáveis de um escopo contido, potencialmente depois que o fluxo de controle deixou esse escopo. Essa última parte é muito útil:
>>> 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
Observe que 12 e 4 "desapareceram" dentro de feg, respectivamente; esse recurso é o que torna feg os fechamentos adequados.
fonte
constant = x
; você poderia apenas fazerreturn y + x
na função aninhada (ou receber o argumento com o nomeconstant
) e funcionaria muito bem; os argumentos são capturados pelo encerramento da mesma forma que os locais sem argumento.Eu gosto desta definição áspera e sucinta :
Eu adicionaria
Decoradores que aceitam parâmetros são um uso comum para fechamentos. Fechamentos são um mecanismo de implementação comum para esse tipo de "fábrica de funções". Eu frequentemente escolho usar fechamentos no padrão de estratégia quando a estratégia é modificada por dados em tempo de execução.
Em uma linguagem que permite a definição de bloco anônimo - por exemplo, Ruby, C # - os fechamentos podem ser usados para implementar (o que significa) novas estruturas de controle. A falta de blocos anônimos está entre as limitações de fechamentos em Python .
fonte
Para ser honesto, eu entendo os fechamentos perfeitamente bem, exceto que nunca fui claro sobre o que exatamente é o "fechamento" e o que é tão "fechamento" nele. Eu recomendo que você desista de procurar qualquer lógica por trás da escolha do termo.
Enfim, aqui está minha explicação:
def foo(): x = 3 def bar(): print x x = 5 return bar bar = foo() bar() # print 5
Uma ideia chave aqui é que o objeto de função retornado de foo retém um gancho para a var local 'x', embora 'x' tenha saído do escopo e deva estar extinto. Esse gancho é para o var em si, não apenas o valor que var tinha no momento, portanto, quando bar é chamado, ele imprime 5, não 3.
Também fique claro que o Python 2.x tem fechamento limitado: não há como modificar 'x' dentro de 'bar' porque escrever 'x = bla' declararia um 'x' local em bar, e não atribuir a 'x' de foo . Este é um efeito colateral da declaração de atribuição = do Python. Para contornar isso, Python 3.0 apresenta a palavra-chave não local:
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
fonte
Nunca ouvi falar de transações sendo usadas no mesmo contexto para explicar o que é um encerramento e realmente não há nenhuma semântica de transação aqui.
É chamado de fechamento porque "fecha sobre" a variável externa (constante) - ou seja, não é apenas uma função, mas um fechamento do ambiente onde a função foi criada.
No exemplo a seguir, chamar o fechamento g depois de alterar x também mudará o valor de x dentro de g, já que g fecha sobre x:
x = 0 def f(): def g(): return x * 2 return g closure = f() print(closure()) # 0 x = 2 print(closure()) # 4
fonte
g()
calcula ,x * 2
mas não retorna nada. Isso deveria serreturn x * 2
. 1, no entanto, para uma explicação para a palavra "encerramento".Aqui está um caso de uso típico para encerramentos - retornos de chamada para elementos da GUI (isso seria uma alternativa para criar uma subclasse da classe do botão). Por exemplo, você pode construir uma função que será chamada em resposta ao pressionamento de um botão e "fechar" sobre as variáveis relevantes no escopo pai que são necessárias para processar o clique. Dessa forma, você pode conectar interfaces bastante complicadas a partir da mesma função de inicialização, criando todas as dependências no encerramento.
fonte
Em Python, um encerramento é uma instância de uma função que possui variáveis vinculadas a ela de forma imutável.
Na verdade, o modelo de dados explica isso em sua descrição do
__closure__
atributo de funções :Para demonstrar isso:
def enclosure(foo): def closure(bar): print(foo, bar) return closure closure_instance = enclosure('foo')
Claramente, sabemos que agora temos uma função apontada a partir do nome da variável
closure_instance
. Ostensivamente, se o chamarmos com um objetobar
, ele deve imprimir a string'foo'
e qualquer que seja a representação da string debar
.Na verdade, a string 'foo' está ligada à instância da função e podemos lê-la diretamente aqui, acessando o
cell_contents
atributo da primeira (e única) célula na tupla do__closure__
atributo:>>> closure_instance.__closure__[0].cell_contents 'foo'
À parte, os objetos de célula são descritos na documentação da API C:
E podemos demonstrar o uso de nosso fechamento, observando que
'foo'
está preso na função e não muda:>>> closure_instance('bar') foo bar >>> closure_instance('baz') foo baz >>> closure_instance('quux') foo quux
E nada pode mudar isso:
>>> closure_instance.__closure__ = None Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: readonly attribute
Funções Parciais
O exemplo dado usa o fechamento como uma função parcial, mas se este for nosso único objetivo, o mesmo objetivo pode ser alcançado com
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
Existem também encerramentos mais complicados que não caberiam no exemplo de função parcial e irei demonstrá-los posteriormente, conforme o tempo permitir.
fonte
# 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
Os critérios a serem atendidos pelos fechamentos são:
# 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
fonte
Aqui está um exemplo de encerramentos 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
fonte
Para mim, "closures" são funções capazes de lembrar o ambiente em que foram criadas. Esta funcionalidade permite que você use variáveis ou métodos dentro do fechamento que, de outra forma, você não conseguiria usar porque não existem mais ou estão fora de alcance devido ao escopo. Vejamos este código em 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"
ele funciona mesmo quando ambos, método "multiplicação" e variável "x", não existem mais. Tudo porque a capacidade de fechamento para lembrar.
fonte
todos nós usamos decoradores em python. Eles são bons exemplos para mostrar o que são funções de encerramento em 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)
aqui o valor final é 12
Aqui, a função wrapper é capaz de acessar o objeto func porque o wrapper é "fechamento lexical", ela pode acessar seus atributos pai. Por isso, ele é capaz de acessar o objeto func.
fonte
Eu gostaria de compartilhar meu exemplo e uma explicação sobre fechamentos. Fiz um exemplo python e duas figuras para demonstrar estados de pilha.
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!')
A saída deste código seria a seguinte:
***** hello ##### good bye! ♥♥♥
Aqui estão duas figuras para mostrar as pilhas e o fechamento anexado ao objeto de função.
quando a função é retornada do fabricante
quando a função é chamada mais tarde
Quando a função é chamada por meio de um parâmetro ou de uma variável não local, o código precisa de ligações de variáveis locais, como margin_top, preenchimento e também a, b, n. A fim de garantir que o código de função funcione, o frame da pilha da função maker que se foi há muito tempo deve estar acessível, o que é feito backup no encerramento que podemos encontrar junto com o objeto de função da mensagem.
fonte
A melhor explicação que já vi de um fechamento foi explicar o mecanismo. Aconteceu mais ou menos assim:
Imagine sua pilha de programa como uma árvore degenerada onde cada nó tem apenas um filho e o nó folha único é o contexto do procedimento em execução no momento.
Agora relaxe a restrição de que cada nó pode ter apenas um filho.
Se você fizer isso, você pode ter uma construção ('rendimento') que pode retornar de um procedimento sem descartar o contexto local (ou seja, não o retira da pilha quando você retorna). Na próxima vez que o procedimento for invocado, a invocação pega o quadro da pilha (árvore) antigo e continua executando de onde parou.
fonte