Criando Funções Funcionais

PythonPythonBeginner
Pratique Agora

This tutorial is from open-source community. Access the source code

💡 Este tutorial foi traduzido do inglês com assistência de IA. Para ver o original, você pode mudar para a versão em inglês

Introdução

Esta seção introduz a ideia de usar funções para criar outras funções.

Introdução

Considere a seguinte função.

def add(x, y):
    def do_add():
        print('Adding', x, y)
        return x + y
    return do_add

Esta é uma função que retorna outra função.

>>> a = add(3,4)
>>> a
<function add.<locals>.do_add at 0x7f27d8a38790>
>>> a()
Adding 3 4
7

Variáveis Locais

Observe como a função interna se refere a variáveis definidas pela função externa.

def add(x, y):
    def do_add():
        ## `x` and `y` are defined above `add(x, y)`
        print('Adding', x, y)
        return x + y
    return do_add

Observe ainda que essas variáveis são de alguma forma mantidas ativas após a conclusão de add().

>>> a = add(3,4)
>>> a
<function do_add at 0x6a670>
>>> a()
Adding 3 4      ## Where are these values coming from?
7

Closures (Fechamentos)

Quando uma função interna é retornada como resultado, essa função interna é conhecida como um closure (fechamento).

def add(x, y):
    ## `do_add` is a closure
    def do_add():
        print('Adding', x, y)
        return x + y
    return do_add

Característica essencial: Um closure retém os valores de todas as variáveis necessárias para que a função seja executada corretamente posteriormente. Pense em um closure como uma função mais um ambiente extra que contém os valores das variáveis das quais ela depende.

Usando Closures (Fechamentos)

Closures (fechamentos) são uma característica essencial do Python. No entanto, seu uso é frequentemente sutil. Aplicações comuns:

  • Uso em funções de callback.
  • Avaliação atrasada.
  • Funções decoradoras (posteriormente).

Avaliação Atrasada (Delayed Evaluation)

Considere uma função como esta:

def after(seconds, func):
    import time
    time.sleep(seconds)
    func()

Exemplo de uso:

def greeting():
    print('Hello Guido')

after(30, greeting)

after executa a função fornecida... mais tarde.

Closures (fechamentos) carregam informações extras.

def add(x, y):
    def do_add():
        print(f'Adding {x} + {y} -> {x+y}')
    return do_add

def after(seconds, func):
    import time
    time.sleep(seconds)
    func()

after(30, add(2, 3))
## `do_add` has the references x -> 2 and y -> 3

Repetição de Código

Closures (fechamentos) também podem ser usados como uma técnica para evitar repetição excessiva de código. Você pode escrever funções que criam código.

Exercício 7.7: Usando Closures para Evitar Repetição

Uma das características mais poderosas dos closures (fechamentos) é seu uso na geração de código repetitivo. Se você voltar ao Exercício 5.7, lembre-se do código para definir uma propriedade com verificação de tipo.

class Stock:
    def __init__(self, name, shares, price):
        self.name = name
        self.shares = shares
        self.price = price
    ...
    @property
    def shares(self):
        return self._shares

    @shares.setter
    def shares(self, value):
        if not isinstance(value, int):
            raise TypeError('Expected int')
        self._shares = value
    ...

Em vez de digitar repetidamente esse código, você pode criá-lo automaticamente usando um closure.

Crie um arquivo typedproperty.py e coloque o seguinte código nele:

## typedproperty.py

def typedproperty(name, expected_type):
    private_name = '_' + name
    @property
    def prop(self):
        return getattr(self, private_name)

    @prop.setter
    def prop(self, value):
        if not isinstance(value, expected_type):
            raise TypeError(f'Expected {expected_type}')
        setattr(self, private_name, value)

    return prop

Agora, experimente definindo uma classe como esta:

from typedproperty import typedproperty

class Stock:
    name = typedproperty('name', str)
    shares = typedproperty('shares', int)
    price = typedproperty('price', float)

    def __init__(self, name, shares, price):
        self.name = name
        self.shares = shares
        self.price = price

Tente criar uma instância e verificar se a verificação de tipo funciona.

>>> s = Stock('IBM', 50, 91.1)
>>> s.name
'IBM'
>>> s.shares = '100'
... should get a TypeError ...
>>>

Exercício 7.8: Simplificando Chamadas de Funções

No exemplo acima, os usuários podem achar chamadas como typedproperty('shares', int) um pouco verbosas para digitar - especialmente se forem repetidas muitas vezes. Adicione as seguintes definições ao arquivo typedproperty.py:

String = lambda name: typedproperty(name, str)
Integer = lambda name: typedproperty(name, int)
Float = lambda name: typedproperty(name, float)

Agora, reescreva a classe Stock para usar essas funções em vez disso:

class Stock:
    name = String('name')
    shares = Integer('shares')
    price = Float('price')

    def __init__(self, name, shares, price):
        self.name = name
        self.shares = shares
        self.price = price

Ah, isso é um pouco melhor. A principal conclusão aqui é que closures (fechamentos) e lambda podem frequentemente ser usados para simplificar o código e eliminar repetições irritantes. Isso geralmente é bom.

Resumo

Parabéns! Você concluiu o laboratório de Retorno de Funções. Você pode praticar mais laboratórios no LabEx para aprimorar suas habilidades.

OSZAR »