Executando verificação de segurança...
1

Decoradores em Python

Olá pessoal, ontem tive um pequeno problema na hora de usar cache em memória no Sanic pra aumentar a velocidade de respostas da minha API, eu não estava conseguindo encontrar material na internet em geral e nem na documentação sobre como fazer isso, então resolvi criar minha própria implementação, e isso me trouxe a fazer a postagem de hoje, espero que gostem.

O que é um decorador?

Em poucas palavras: Um decorador é uma função que "embrulha" outra função na hora de sua definição.

Isto é útil para filtragem de dados de entrada e outras coisas(no meu caso foi armazenamento de um retorno da função para ser usado posteriormente).

Um exemplo de uso é em bots do Telegram, você pode criar um decorador que só permite a execução de um comando caso quem tenha enviado seja um administrador.

Criando um decorador

A criação de decoradores em python é bem fácil, existem dois tipos de decoradores possiveis de serem criados, o mais simples apenas marca uma função e executa um código estático nela, o segundo pode receber argumentos e assim fazer mais decisões sobre o que deve ou não ser feito, aqui eu vou ensinar as duas formas para que não gastem processamento atoa :)

Decorador simples

O decorador simples é apenas uma closure com uma função de resposta marcada como "embrulho", a definição é dada usando uma função normal que recebe a função desenvolvida pelo usúario como argumento e dentro dessa função principal é definida outra usando o decorador que marca ela como "embrulho", logo depois essa função marcada é devolvida como resposta da função principal, veja um exemplo:

# Esse é o decorador padrão que marca como "embrulho"
from functools import wraps

def meu_decorador(func):
    @wraps(func)
    def embrulho(*args, **kwargs):
        print("Essa função foi embrulhada com o decorador simples")
        # Você também pode salvar o retorno da função e fazer o que quiser com ele
        return func(*args, **kwargs)
    return embrulho

Para usar esse tipo de decorador você não pode chamá-lo, apenas marcar, se chamá-lo ira ocorrer um erro por falta do argumento func e não ira funcionar!

Veja um exemplo de uso:

@meu_decorador
def ola():
    print("Olá, Mundo!")

ola()

A execução desse código vai resultar na seguinte saida no terminal:

Essa função foi embrulhada com o decorador simples
Olá, Mundo!

Caso ainda duvide de ocorrer um erro se chamar o decorador é só você mesmo testar por ai, use @meu_decorador() no lugar de @meu_decorador.

Obs: Se você definir o parâmetro func manualmente é possivel usar o decorador como closure normal, mas isso é meio inútil.

Decorador que aceita argumentos

Agora chegamos no decorador legal, esse tem mais utilidade que o anterior por poder receber argumentos que ajudam nas decisões.

O jeito de fazer esse é praticamente o mesmo, mas você cria uma função que contem o decorador simples e retorna ele como resposta, nessa função você pode definir parâmetros para receber os argumentos. Ao cotrário da anterior, nessa versão você não recebe a função desenvolvida na principal, apenas no decorador que vai ser retornado.

Veja um exemplo de como esse é desenvolvido:

from functools import wraps
from time import sleep

def demora(n: int):
    def decorador(func):
        primeira_execucao: bool = True
        @wraps(func)
        def embrulho(*args, **kwargs):
            nonlocal primeira_execucao
            print("Essa função foi embrulhada pelo decorador que recebe argumentos")
            if primeira_execucao:
                print(f"Esperando {n} segundos para que a função seja executada pela primeira vez")
                sleep(n)
                primeira_execucao = False
            return func(*args, **kwargs)
        return embrulho
    return decorador

Aqui nós recebemos um argumento n que diz o tempo demorado para a primeira execução da função, e guardamos a informação de se é a primeira execução em uma variável no escopo do decorador que é usada dentro do embrulho, mas para que essa variável seja manipulada é preciso o uso da palavra chave nonlocal já que a variável não é local e nem global, ela esta um escopo acima de onde esta sendo usada. Um detalhe é que qualquer informação que deseje ser permanente deve estar no escopo do decorador, e não no do embrulho.

Agora caso você use esse decorador em uma função, ela ira demorar n segundos para ser executada pela primeira vez e nas posteriores ira executar de imediato, veja mais esse exemplo:

@demora(2)
def ola2(nome: str):
    print(f"Olá, {nome}!")

ola2("João")
ola2("Maria")

Esse código ira resultar na seguinte saída:

Essa função foi embrulhada pelo decorador que recebe argumentos
Esperando 2 segundos para que a função seja executada pela primeira vez
Olá, João!
Essa função foi embrulhada pelo decorador que recebe argumentos
Olá, Maria!

Veja que na segunda execução ele já não tem mais a demora de 2 segundos que na primeira vez, isso é porque guardamos a informação de que era primeira execução em uma variável no escopo do decorador, que não é executado toda vez que a função é chamada, apenas em sua criação.

É isso! Espero que tenham gostado e obrigado por ler ate aqui ;)

Carregando publicação patrocinada...
1
1