Eu já vi e usei funções aninhadas no Python, e elas correspondem à definição de encerramento. Então, por que eles são chamados em nested functions
vez de closures
?
As funções aninhadas não são encerramentos porque não são usadas pelo mundo externo?
ATUALIZAÇÃO: Eu estava lendo sobre fechamentos e isso me fez pensar sobre esse conceito em relação ao Python. Pesquisei e encontrei o artigo mencionado por alguém em um comentário abaixo, mas não consegui entender completamente a explicação desse artigo; é por isso que estou fazendo essa pergunta.
python
closures
nested-function
Srikar Appalaraju
fonte
fonte
Respostas:
Um fechamento ocorre quando uma função tem acesso a uma variável local a partir de um escopo que encerrou sua execução.
Quando
make_printer
é chamado, um novo quadro é colocado na pilha com o código compilado para aprinter
função como constante e o valormsg
como local. Em seguida, ele cria e retorna a função. Como a função fazprinter
referência àmsg
variável, ela é mantida ativa após omake_printer
retorno da função.Portanto, se suas funções aninhadas não
então eles não são fechamentos.
Aqui está um exemplo de uma função aninhada que não é um fechamento.
Aqui, estamos vinculando o valor ao valor padrão de um parâmetro. Isso ocorre quando a função
printer
é criada e, portanto, nenhuma referência ao valor demsg
externo aprinter
precisa ser mantida apósmake_printer
retornos.msg
é apenas uma variável local normal da funçãoprinter
neste contexto.fonte
self
. (Em JavaScript / Python, isso é quase verdade.) #i
] a partir de um escopo". refere-se, isto é, pode inspecionar (ou alterar)i
o valor de, mesmo se / quando esse escopo "tiver terminado sua execução", isto é, a execução de um programa foi transferida para outras partes do código. O bloco ondei
está definido não existe mais, mas as funções referentes ai
ainda podem fazê-lo. Isso é comumente descrito como "fechando sobre a variáveli
". Para não lidar com as variáveis específicas, ele pode ser implementado como fechamento por todo o quadro de ambiente em que a variável está definida.A pergunta já foi respondida por aaronasterling
No entanto, alguém pode estar interessado em como as variáveis são armazenadas sob o capô.
Antes de vir para o trecho:
Encerramentos são funções que herdam variáveis de seu ambiente anexo. Quando você passa um retorno de chamada de função como argumento para outra função que executará E / S, essa função de retorno de chamada será chamada posteriormente, e essa função - quase magicamente - lembrará o contexto em que foi declarado, juntamente com todas as variáveis disponíveis. nesse contexto.
Se uma função não usa variáveis livres, ela não forma um fechamento.
Se houver outro nível interno que utilize variáveis livres - todos os níveis anteriores salvam o ambiente lexical (exemplo no final)
atributos de função
func_closure
em python <3.X ou__closure__
em python> 3.X salvam as variáveis livres.Toda função no python possui esses atributos de fechamento, mas não salva nenhum conteúdo se não houver variáveis livres.
exemplo: de atributos de fechamento, mas nenhum conteúdo interno, pois não há variável livre.
NB: VARIÁVEL GRÁTIS É DEVE CRIAR UM ENCERRAMENTO.
Vou explicar usando o mesmo trecho como acima:
E todas as funções Python têm um atributo de fechamento, então vamos examinar as variáveis anexas associadas a uma função de fechamento.
Aqui está o atributo
func_closure
para a funçãoprinter
o
closure
atributo retorna uma tupla de objetos de célula que contêm detalhes das variáveis definidas no escopo em anexo.O primeiro elemento no func_closure que poderia ser None ou uma tupla de células que contêm ligações para as variáveis livres da função e é somente leitura.
Aqui na saída acima, você pode ver
cell_contents
, vamos ver o que ele armazena:Então, quando chamamos a função
printer()
, ela acessa o valor armazenado dentro docell_contents
. Foi assim que obtivemos o resultado como 'Foo!'Mais uma vez, explicarei o uso do snippet acima com algumas alterações:
No snippet acima, não imprimo msg dentro da função da impressora, para que não crie nenhuma variável livre. Como não há variável livre, não haverá conteúdo dentro do fechamento. Isso é exatamente o que vemos acima.
Agora vou explicar outro trecho diferente para limpar tudo
Free Variable
comClosure
:Portanto, vemos que uma
func_closure
propriedade é uma tupla de células de fechamento , podemos referenciá-las e seu conteúdo explicitamente - uma célula tem a propriedade "cell_contents"Aqui quando chamamos
inn
, ele se refere a todas as variáveis livres de salvamento, paraI am free variable
fonte
func_closure
agora é chamado__closure__
, de maneira semelhante aos vários outrosfunc_*
atributos.__closure_
está disponível em Python 2.6+ para compatibilidade com Python 3.__closure__
objeto que é o fechamento.O Python tem um suporte fraco para o fechamento. Para entender o que quero dizer, use o seguinte exemplo de contador usando fechamento com JavaScript:
O fechamento é bastante elegante, pois fornece às funções escritas assim a capacidade de ter "memória interna". A partir do Python 2.7, isso não é possível. Se você tentar
Você receberá um erro dizendo que x não está definido. Mas como pode ser isso, se tiver sido demonstrado por outras pessoas que você pode imprimi-lo? Isso ocorre devido à maneira como o Python gerencia o escopo da variável de funções. Embora a função interna possa ler as variáveis da função externa, ela não pode escrevê- las.
É realmente uma pena. Mas com apenas o fechamento somente leitura, você pode pelo menos implementar o padrão do decorador de funções para o qual o Python oferece açúcar sintático.
Atualizar
Como foi apontado, existem maneiras de lidar com as limitações de escopo do python e vou expor algumas.
1. Use o
global
palavra chave (em geral não recomendada).2. No Python 3.x, use a
nonlocal
palavra - chave (sugerida por @unutbu e @leewz)3. Defina uma classe modificável simples
Object
e crie um
Object scope
interiorinitCounter
para armazenar as variáveisComo
scope
é realmente apenas uma referência, as ações executadas com seus campos não se modificam realmentescope
, portanto, nenhum erro ocorre.4. Uma maneira alternativa, como apontou @unutbu, seria definir cada variável como uma matriz (
x = [0]
) e modificar seu primeiro elemento (x[0] += 1
). Novamente, nenhum erro surge porquex
ele próprio não é modificado.5. Como sugerido por @raxacoricofallapatorius, você pode fazer
x
uma propriedade decounter
fonte
x = [0]
no escopo externo e usá-lox[0] += 1
no escopo interno. No Python3, você pode manter seu código como está e usar a palavra-chave não - local .x
aponta permanece exatamente a mesma, mesmo que você chameinc()
ou seja o que for, e você não gravou efetivamente na variável.x
uma propriedade decounter
.nonlocal
palavra - chave, que é parecidaglobal
com as variáveis de uma função externa. Isso permitirá que uma função interna reconecte um nome de suas funções externas. Eu acho que "vincular ao nome" é mais preciso do que "modificar a variável".O Python 2 não tinha encerramentos - tinha soluções alternativas que se assemelhavam fechamentos.
Já existem muitos exemplos nas respostas - copiar variáveis para a função interna, modificar um objeto na função interna etc.
No Python 3, o suporte é mais explícito - e sucinto:
Uso:
A
nonlocal
palavra-chave vincula a função interna à variável externa mencionada explicitamente, incluindo-a. Daí mais explicitamente um 'fechamento'.fonte
Eu tive uma situação em que precisava de um espaço para nome separado, mas persistente. Eu usei aulas. Eu não faço de outra maneira. Nomes segregados mas persistentes são fechamentos.
fonte
Dá:
Este é um exemplo do que é um fechamento e como ele pode ser usado.
fonte
Eu gostaria de oferecer outra comparação simples entre exemplo python e JS, se isso ajudar a tornar as coisas mais claras.
JS:
e executando:
Pitão:
e executando:
Razão: Como muitos outros disseram acima, em python, se houver uma atribuição no escopo interno para uma variável com o mesmo nome, uma nova referência no escopo interno será criada. Não é assim com JS, a menos que você declare explicitamente um com a
var
palavra - chave.fonte