Pular para o conteúdo principal
InicioTutoriaisPython

Tutorial de decoradores Python

Neste tutorial, você aprenderá a implementar decoradores em Python.
Actualizado 11 de set. de 2024  · 7 min leer

Um decorador é um padrão de design em Python que permite que um usuário adicione novas funcionalidades a um objeto existente sem modificar sua estrutura. Normalmente, os decoradores são aplicados a funções e desempenham um papel fundamental no aprimoramento ou na modificação do comportamento das funções. Tradicionalmente, os decoradores são colocados antes da definição de uma função que você deseja decorar. Neste tutorial, demonstraremos como usar decoradores de forma eficaz nas funções Python.

As funções em Python são cidadãos de primeira classe. Isso significa que eles suportam operações como ser passado como argumento, retornado de uma função, modificado e atribuído a uma variável. Essa propriedade é fundamental, pois permite que as funções sejam tratadas como qualquer outro objeto no Python, possibilitando maior flexibilidade na programação.

Para executar facilmente todos os exemplos de código deste tutorial, você pode criar gratuitamente uma pasta de trabalho do DataLab que tenha o Python pré-instalado e contenha todos os exemplos de código. Para praticar mais os decoradores, confira este exercício prático do DataCamp.

Aprenda Python do zero

Domine o Python para a ciência de dados e adquira habilidades que estão em alta.
Comece a Aprender De Graça

Atribuição de funções a variáveis

Para começar, criamos uma função que adicionará um a um número sempre que for chamada. Em seguida, atribuiremos a função a uma variável e usaremos essa variável para chamar a função.

def plus_one(number):
    return number + 1

add_one = plus_one
add_one(5)
6

Definição de funções dentro de outras funções 

A seguir, ilustraremos como você pode definir uma função dentro de outra função no Python. Fique comigo, pois em breve você descobrirá como tudo isso é relevante para criar e entender os decoradores em Python.

def plus_one(number):
    def add_one(number):
        return number + 1


    result = add_one(number)
    return result
plus_one(4)
5

Passagem de funções como argumentos para outras funções

As funções também podem ser passadas como parâmetros para outras funções. Vamos ilustrar isso a seguir.

def plus_one(number):
    return number + 1

def function_call(function):
    number_to_add = 5
    return function(number_to_add)

function_call(plus_one)
6

Funções que retornam outras funções

Uma função também pode gerar outra função. Mostraremos isso a seguir com um exemplo.

def hello_function():
    def say_hi():
        return "Hi"
    return say_hi
hello = hello_function()
hello()
'Hi'

As funções aninhadas têm acesso ao escopo da variável da função envolvente

O Python permite que uma função aninhada acesse o escopo externo da função que a envolve. Esse é um conceito fundamental em decoradores: esse padrão é conhecido como Closure.

def print_message(message):
    "Enclosong Function"
    def message_sender():
        "Nested Function"
        print(message)

    message_sender()

print_message("Some random message")
Some random message

Criando Decoradores

Com esses pré-requisitos fora do caminho, vamos criar um decorador simples que converterá uma frase em maiúsculas. Fazemos isso definindo um wrapper dentro de uma função anexa. Como você pode ver, é muito semelhante à função dentro de outra função que criamos anteriormente.

def uppercase_decorator(function):
    def wrapper():
        func = function()
        make_uppercase = func.upper()
        return make_uppercase

    return wrapper

Nossa função de decorador recebe uma função como argumento e, portanto, devemos definir uma função e passá-la ao nosso decorador. Aprendemos anteriormente que podemos atribuir uma função a uma variável. Usaremos esse truque para chamar nossa função de decorador.

def say_hi():
    return 'hello there'

decorate = uppercase_decorator(say_hi)
decorate()
'HELLO THERE'

No entanto, o Python oferece uma maneira muito mais fácil de aplicar decoradores. Basta usar o símbolo @ antes da função que você deseja decorar. Vamos mostrar isso na prática a seguir.

@uppercase_decorator
def say_hi():
    return 'hello there'

say_hi()
'HELLO THERE'

Aplicação de vários decoradores a uma única função

Você pode usar vários decoradores em uma única função. No entanto, os decoradores serão aplicados na ordem em que os chamamos. A seguir, definiremos outro decorador que divide a frase em uma lista. Em seguida, aplicaremos os decoradores uppercase_decorator e split_string a uma única função.

import functools
def split_string(function):
    @functools.wraps(function)
    def wrapper():
        func = function()
        splitted_string = func.split()
        return splitted_string

    return wrapper 
@split_string
@uppercase_decorator
def say_hi():
    return 'hello there'
say_hi()
['HELLO', 'THERE']

Na saída acima, notamos que a aplicação dos decoradores é feita de baixo para cima. Se tivéssemos trocado a ordem, teríamos visto um erro, pois as listas não têm um atributo upper. A frase foi primeiro convertida em letras maiúsculas e depois dividida em uma lista.

Observação: Ao empilhar decoradores, é uma prática comum usar o site functools.wraps para garantir que os metadados da função original sejam preservados durante todo o processo de empilhamento. Isso ajuda a manter a clareza e a consistência na depuração e na compreensão das propriedades da função decorada.

Aceitação de argumentos em funções do Decorator

Às vezes, talvez seja necessário definir um decorador que aceite argumentos. Para isso, você passa os argumentos para a função wrapper. Os argumentos serão então passados para a função que está sendo decorada no momento da chamada.

def decorator_with_arguments(function):
    def wrapper_accepting_arguments(arg1, arg2):
        print("My arguments are: {0}, {1}".format(arg1,arg2))
        function(arg1, arg2)
    return wrapper_accepting_arguments


@decorator_with_arguments
def cities(city_one, city_two):
    print("Cities I love are {0} and {1}".format(city_one, city_two))

cities("Nairobi", "Accra")

My arguments are: Nairobi, Accra Cities I love are Nairobi and Accra

Observação: É essencial que você garanta que o número de argumentos no decorador (arg1, arg2 neste exemplo) corresponda ao número de argumentos na função envolvida (cities neste exemplo). Esse alinhamento é fundamental para evitar erros e garantir a funcionalidade adequada ao usar decoradores com argumentos.

Definição de decoradores de uso geral

Para definir um decorador de uso geral que possa ser aplicado a qualquer função, usamos args e **kwargs. args e **kwargs coletam todos os argumentos posicionais e de palavra-chave e os armazenam nas variáveis args e kwargs. args e kwargs nos permitem passar quantos argumentos quisermos durante as chamadas de função.

def a_decorator_passing_arbitrary_arguments(function_to_decorate):
    def a_wrapper_accepting_arbitrary_arguments(*args,**kwargs):
        print('The positional arguments are', args)
        print('The keyword arguments are', kwargs)
        function_to_decorate(*args)
    return a_wrapper_accepting_arbitrary_arguments

@a_decorator_passing_arbitrary_arguments
def function_with_no_argument():
    print("No arguments here.")

function_with_no_argument()
The positional arguments are ()
The keyword arguments are {}
No arguments here.

Vamos ver como usar o decorador usando argumentos posicionais.

@a_decorator_passing_arbitrary_arguments
def function_with_arguments(a, b, c):
    print(a, b, c)

function_with_arguments(1,2,3)
The positional arguments are (1, 2, 3)
The keyword arguments are {}
1 2 3

Os argumentos de palavras-chave são passados usando palavras-chave. Uma ilustração disso é mostrada abaixo.

@a_decorator_passing_arbitrary_arguments
def function_with_keyword_arguments():
    print("This has shown keyword arguments")

function_with_keyword_arguments(first_name="Derrick", last_name="Mwiti")
The positional arguments are ()
The keyword arguments are {'first_name': 'Derrick', 'last_name': 'Mwiti'}
This has shown keyword arguments

Observação: O uso de **kwargs no decorador permite que ele manipule argumentos de palavras-chave. Isso torna o decorador de uso geral versátil e capaz de lidar com uma variedade de tipos de argumentos durante as chamadas de função.

Passagem de argumentos para o Decorator

Agora vamos ver como passar argumentos para o decorador em si. Para isso, definimos um criador de decoradores que aceita argumentos e, em seguida, definimos um decorador dentro dele. Em seguida, definimos uma função wrapper dentro do decorador, como fizemos anteriormente.

def decorator_maker_with_arguments(decorator_arg1, decorator_arg2, decorator_arg3):
    def decorator(func):
        def wrapper(function_arg1, function_arg2, function_arg3) :
            "This is the wrapper function"
            print("The wrapper can access all the variables\n"
                  "\t- from the decorator maker: {0} {1} {2}\n"
                  "\t- from the function call: {3} {4} {5}\n"
                  "and pass them to the decorated function"
                  .format(decorator_arg1, decorator_arg2,decorator_arg3,
                          function_arg1, function_arg2,function_arg3))
            return func(function_arg1, function_arg2,function_arg3)

        return wrapper

    return decorator

pandas = "Pandas"
@decorator_maker_with_arguments(pandas, "Numpy","Scikit-learn")
def decorated_function_with_arguments(function_arg1, function_arg2,function_arg3):
    print("This is the decorated function and it only knows about its arguments: {0}"
           " {1}" " {2}".format(function_arg1, function_arg2,function_arg3))

decorated_function_with_arguments(pandas, "Science", "Tools")
The wrapper can access all the variables
    - from the decorator maker: Pandas Numpy Scikit-learn
    - from the function call: Pandas Science Tools
and pass them to the decorated function
This is the decorated function, and it only knows about its arguments: Pandas Science Tools

Depuração de decoradores

Como já observamos, os decoradores envolvem as funções. O nome da função original, sua docstring e a lista de parâmetros são ocultados pelo fechamento do wrapper: Por exemplo, quando tentarmos acessar os metadados do decorated_function_with_arguments, veremos os metadados do fechamento do wrapper. Isso representa um desafio para a depuração.

decorated_function_with_arguments.__name__
'wrapper'
decorated_function_with_arguments.__doc__
'This is the wrapper function'

Para resolver esse desafio, o Python fornece um decorador functools.wraps. Esse decorador copia os metadados perdidos da função não decorada para o fechamento decorado. Vamos mostrar como você faria isso.

import functools

def uppercase_decorator(func):
    @functools.wraps(func)
    def wrapper():
        return func().upper()
    return wrapper
@uppercase_decorator
def say_hi():
    "This will say hi"
    return 'hello there'

say_hi()
'HELLO THERE'

Quando verificamos os metadados do say_hi, percebemos que agora ele está se referindo aos metadados da função e não aos metadados do wrapper.

say_hi.__name__
'say_hi'
say_hi.__doc__
'This will say hi'

É aconselhável e uma boa prática que você sempre use functools.wraps ao definir decoradores. Isso poupará a você muita dor de cabeça na depuração.

Resumo dos decoradores Python

Os decoradores alteram dinamicamente a funcionalidade de uma função, método ou classe sem precisar usar diretamente as subclasses ou alterar o código-fonte da função que está sendo decorada. O uso de decoradores em Python também garante que seu código seja DRY (Don't Repeat Yourself). Os decoradores têm vários casos de uso, como:

  • Autorização em estruturas Python, como Flask e Django
  • Registro em log
  • Medição do tempo de execução
  • Sincronização

Para saber mais sobre os decoradores do Python, consulte a Biblioteca de decoradores do Python.

Temas

Saiba mais sobre Python

Course

Writing Functions in Python

4 hr
88.8K
Learn to use best practices to write maintainable, reusable, complex functions with good documentation.
See DetailsRight Arrow
Start Course
Ver maisRight Arrow
Relacionado

tutorial

Tutorial de funções Python

Um tutorial sobre funções em Python que aborda como escrever funções, como chamá-las e muito mais!
Karlijn Willems's photo

Karlijn Willems

14 min

tutorial

Tutorial de indexação de lista Python()

Neste tutorial, você aprenderá exclusivamente sobre a função index().
Sejal Jaiswal's photo

Sejal Jaiswal

6 min

tutorial

Função do sublinhado (_) no tutorial de Python

Neste tutorial, você aprenderá sobre os usos do sublinhado (_) em python.
Hafeezul Kareem Shaik's photo

Hafeezul Kareem Shaik

8 min

tutorial

Tutorial de strings em Python

Neste tutorial, você aprenderá tudo sobre as cadeias de caracteres do Python: fatiamento e encadeamento, manipulação e formatação com a classe Formatter, cadeias de caracteres f, modelos e muito mais!
Sejal Jaiswal's photo

Sejal Jaiswal

16 min

tutorial

Tutorial do Python pandas: O guia definitivo para iniciantes

Você está pronto para começar sua jornada com os pandas? Aqui está um guia passo a passo sobre como você pode começar.
Vidhi Chugh's photo

Vidhi Chugh

15 min

tutorial

Operadores em Python

Este tutorial aborda os diferentes tipos de operadores em Python, sobrecarga de operadores, precedência e associatividade.
Théo Vanderheyden's photo

Théo Vanderheyden

9 min

See MoreSee More