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 :
Nenhum ou uma tupla de células que contêm ligações para as variáveis livres da função. Somente leitura
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 objeto bar
, 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:
Objetos de "célula" são usados para implementar variáveis referenciadas por múltiplos escopos
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
>>> 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.
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)