Neste tutorial, você aprenderá sobre o fechamento Python, como definir um fechamento e os motivos pelos quais deve usá-lo.
Variável não local em uma função aninhada
Antes de entrar no que é um fechamento, primeiro temos que entender o que é uma função aninhada e uma variável não local.
Uma função definida dentro de outra função é chamada de função aninhada. Funções aninhadas podem acessar variáveis do escopo envolvente.
Em Python, essas variáveis não locais são somente leitura por padrão e devemos declará-las explicitamente como não locais (usando a palavra-chave nonlocal) para modificá-las.
A seguir está um exemplo de uma função aninhada acessando uma variável não local.
def print_msg(msg): # This is the outer enclosing function def printer(): # This is the nested function print(msg) printer() # We execute the function # Output: Hello print_msg("Hello")
Resultado
Olá
Podemos ver que a printer()
função aninhada foi capaz de acessar a variável msg não local da função envolvente.
Definindo uma função de fechamento
No exemplo acima, o que aconteceria se a última linha da função print_msg()
retornasse a printer()
função em vez de chamá-la? Isso significa que a função foi definida da seguinte forma:
def print_msg(msg): # This is the outer enclosing function def printer(): # This is the nested function print(msg) return printer # returns the nested function # Now let's try calling this function. # Output: Hello another = print_msg("Hello") another()
Resultado
Olá
Isso é incomum.
A print_msg()
função foi chamada com a string "Hello"
e a função retornada foi associada ao nome outro. Ao chamar another()
, a mensagem ainda era lembrada, embora já tivéssemos finalizado a execução da print_msg()
função.
Essa técnica pela qual alguns dados ( "Hello
neste caso) são anexados ao código é chamada de encerramento em Python .
Este valor no escopo delimitador é lembrado mesmo quando a variável sai do escopo ou a própria função é removida do namespace atual.
Tente executar o seguinte no shell Python para ver a saída.
>>> del print_msg >>> another() Hello >>> print_msg("Hello") Traceback (most recent call last):… NameError: name 'print_msg' is not defined
Aqui, a função retornada ainda funciona mesmo quando a função original foi excluída.
Quando temos encerramentos?
Como visto no exemplo acima, temos um encerramento no Python quando uma função aninhada faz referência a um valor em seu escopo envolvente.
Os critérios que devem ser atendidos para criar o fechamento em Python são resumidos nos pontos a seguir.
- Devemos ter uma função aninhada (função dentro de uma função).
- A função aninhada deve se referir a um valor definido na função envolvente.
- A função envolvente deve retornar a função aninhada.
Quando usar fechos?
Então, para que servem os fechos?
Os fechamentos podem evitar o uso de valores globais e fornecem alguma forma de ocultação de dados. Ele também pode fornecer uma solução orientada a objetos para o problema.
Quando há poucos métodos (um método na maioria dos casos) a serem implementados em uma classe, os fechamentos podem fornecer uma solução alternativa e mais elegante. Mas quando o número de atributos e métodos aumenta, é melhor implementar uma classe.
Aqui está um exemplo simples em que um encerramento pode ser mais preferível do que definir uma classe e fazer objetos. Mas a preferência é toda sua.
def make_multiplier_of(n): def multiplier(x): return x * n return multiplier # Multiplier of 3 times3 = make_multiplier_of(3) # Multiplier of 5 times5 = make_multiplier_of(5) # Output: 27 print(times3(9)) # Output: 15 print(times5(3)) # Output: 30 print(times5(times3(2)))
Resultado
27 15 30
Os decoradores Python também fazem uso extensivo de encerramentos.
Em uma nota final, é bom salientar que os valores que são incluídos na função de fechamento podem ser encontrados.
Todos os objetos de função têm um __closure__
atributo que retorna uma tupla de objetos de célula se for uma função de fechamento. Referindo-nos ao exemplo acima, sabemos times3
e times5
somos funções de fechamento.
>>> make_multiplier_of.__closure__ >>> times3.__closure__ (,)
O objeto de célula possui o atributo cell_contents que armazena o valor fechado.
>>> times3.__closure__(0).cell_contents 3 >>> times5.__closure__(0).cell_contents 5