Fechamentos de Python: como usar e por quê?

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 ( "Helloneste 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 times3e times5somos 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

Artigos interessantes...