Um dos fatores que tornam o Python uma linguagem de programação tão poderosa são os magic methods. Em alguns círculos de Pythonistas, você pode ouvi-los sendo chamados de ‘dunder methods’, por causa do padrão de dois underlines.
O papel deles no Python é realizar um procedimento conhecido como ‘sobrecarga de operadores’ (operator overloading), que basicamente dá um significado estendido a um operador predefinido. Em outras palavras, usamos a sobrecarga de operadores para adicionar comportamentos personalizados às nossas classes e habilitar seu uso com os operadores e funcionalidades nativas do Python.
Neste artigo, vamos nos aprofundar nos magic methods do Python, abordando:
A diferença entre métodos e funções
O que são magic methods
Alguns exemplos em Python.
Confira este caderno no DataLab para acompanhar o código usado neste tutorial.
Métodos vs. funções em Python
Antes de explicar o que são magic methods, precisamos entender as diferenças entre método e função em Python.
O Python usa métodos para representar o comportamento de um objeto. Assim, métodos estão associados a um objeto e executam operações sobre os dados dentro desse objeto. Outro ponto distintivo é o parâmetro self, que deve sempre aparecer como o primeiro parâmetro passado ao método.
Observação: não precisa ser literalmente a palavra self como primeiro parâmetro, mas a comunidade Pythonista concorda que é a melhor prática.
# Exemplo de métodoclassHuman:# Este é um métododefwalk(self):print("I am walking")Tecnologia
Métodos dependem da classe à qual estão associados. Portanto, para chamar um método, é preciso criar uma instância da classe antes de acessá-lo com o operador de ponto.
# Isso vai funcionar
joe = Human()
joe.walk()"""
I am walking
"""# Isso vai gerar um NameError
walk()---------------------------------------------------------------------------
NameError Traceback (most recent call last)/tmp/ipykernel_153/2327344440.py in<module>45# Isso vai gerar um NameError---->6 walk()
NameError: name 'walk'isnot definedTecnologia
Uma função, por outro lado, é diferente: não precisamos criar uma instância de classe.
Funções em Python são passos executados como um bloco de código apenas quando chamadas. Elas são definidas de forma independente de qualquer objeto de classe, o que significa que podem ser chamadas diretamente pelo nome. Como não estão associadas a nenhuma classe, não precisamos passar self como parâmetro – parâmetros são opcionais.
Em essência, uma função é simplesmente um conjunto de instruções bem definidas que o interpretador usa para executar uma tarefa.
# Exemplo de funçãodefrun():print("I am running.")
run()"""
I am running.
"""Tecnologia
Como exploramos no artigo Como chamar e escrever funções, funções também vêm em dois tipos: nativas e definidas pelo usuário.
Funções nativas (built-ins): funções incluídas na biblioteca padrão do Python.
Funções definidas pelo usuário: funções definidas pelo usuário - o Python permite definir e usar suas próprias funções, que podem ser acessadas em um programa.
Em muitos aspectos, métodos e funções são idênticos – ambos são definidos com a instrução def. A diferença é que métodos estão associados a um objeto e operam sobre os dados contidos em uma classe, enquanto funções são definidas de forma independente e podem ser chamadas pelo nome em qualquer lugar do programa.
O que são os magic methods do Python?
No início do tutorial, mencionamos que magic methods também são chamados de dunder methods por conta do padrão de dois underlines. Isso quer dizer que todos os magic methods em Python começam e terminam com dois underlines.
O magic method mais conhecido é o __init__, que permite inicializar uma classe com atributos específicos.
classEmployee:def__init__(self, name, age, role):
self.name = name
self.age = age
self.role = roleTecnologia
Para instanciar essa classe, precisamos especificar os atributos name, age e role da instância. Por exemplo:
Se não fizermos isso, será levantado um TypeError.
O método __init__ faz parte da categoria de magic methods de inicialização e construção, mas é só um entre vários. A tabela abaixo mostra outros métodos dessa categoria.
__new__(cls, other)
Usado para criar uma nova instância de uma classe
__init__(self, other)
Chamado depois que uma nova instância é criada; todos os atributos passados ao construtor da classe serão usados como atributos.
__del__(self)
Também conhecido como destrutor; chamado quando uma instância vai ser destruída.
A forma mais simples de entender os magic methods é como um contrato entre a sua implementação e o interpretador do Python. Uma das cláusulas exige que o Python realize certas ações nos bastidores a partir de determinadas situações, como inicializar uma instância de classe com objetos específicos.
Em outras palavras, não é sua responsabilidade invocar um magic method diretamente – o Python faz isso por você, por baixo do capô.
Métodos de representação em string
Python é uma linguagem de programação orientada a objetos (POO) – tudo em Python é um objeto. Quando um novo objeto é criado, implicitamente criamos um objeto relacionado porque todas as classes herdam de object.
Observação: você também pode tornar a herança explícita passando a classe object para a sua classe.
# FuncionaclassCar:pass
# Também funcionaclassVehicle(object):pass
car = Car()
vehicle = Vehicle()
print(car, vehicle, sep="\n")"""
<__main__.Car object at 0x7f80449dc0a0>
<__main__.Vehicle object at 0x7f80449dc1f0>
"""Tecnologia
No código acima, criamos duas classes vazias, mas o interpretador ainda soube o que imprimir quando chamamos print. Isso é possível porque todas as classes herdam da classe object (a mãe de todas as classes no Python), que possui um magic method chamado __repr__, o qual o print chama para retornar um valor ao nosso programa principal.
Quem já conhece herança sabe que uma classe filha pode sobrescrever sua classe pai definindo seu próprio método com o mesmo nome.
# Sobrescrevendo o __repr__ de objectclassVehicle:def__repr__(self):returnf"{self.__class__.__qualname__}"
vehicle = Vehicle()print(vehicle)"""
Vehicle
"""Tecnologia
Você também pode usar o magic method __str__ para criar uma representação em string que é chamada pelo print.
Na verdade, o __repr__ funciona como comportamento de fallback do __str__. Assim, quando chamamos print, o Python primeiro procura o método __str__ definido na sua classe e, se não encontrar, recorre ao __repr__.
# Usando __str__ para representação em stringclassVehicle:def__str__(self):returnf"{self.__class__.__qualname__}"
vehicle = Vehicle()print(vehicle)"""
Vehicle
"""Tecnologia
A tabela abaixo lista outros magic methods de representação em string.
__str__(self)
Chamado pela função nativa str() do Python e pelo print para exibir a representação informal em string de um objeto. Observe que não precisa ser uma expressão Python válida.
__repr__(self)
Chamado pela função nativa repr() do Python e por conversões de string para exibir a representação oficial em string de um objeto. Idealmente, deve parecer uma expressão Python válida, mas, se não for possível, uma string com uma descrição útil é preferida.
__unicode__(self)
Chamado para implementar a função nativa unicode() do Python e retorna um objeto Unicode.
__hash__(self)
Chamado pela função nativa hash() do Python e por operações em membros de coleções com hash. O método deve retornar um inteiro.
__nonzero__(self)
Chamado pela operação nativa bool() do Python para implementar testes de verdade. O método deve retornar True ou False (ou seus equivalentes inteiros 1 ou 0).
Métodos "mágicos" de encapsulamento
Encapsulamento é um dos conceitos fundamentais da programação orientada a objetos (POO). Ele se refere a agrupar dados com os métodos que operam sobre esses dados ou restringir o acesso direto a certos componentes dentro de um objeto.
Desenvolvedores vindos de outras linguagens de POO costumam criticar o fato de o Python não adotar encapsulamento de forma tão explícita nas classes. Eles estão acostumados a modificadores para métodos ou campos que permitem definir, por exemplo, atributos privados com getters e setters públicos. No Python, boa parte do encapsulamento acontece via magic methods.
Por exemplo, __setattr__ é uma solução de encapsulamento que nos permite definir o comportamento da atribuição a um atributo, exista ele previamente ou não. Em outras palavras, é possível definir regras para mudanças nos valores dos atributos.
import string
classAlphabet():def__setattr__(self, name, value):
self.__dict__[name]= value.upper()
a = Alphabet()# Atribuindo um atributo que não existe# O setattr deve convertê-lo para maiúsculas mesmo tendo sido definido em minúsculas
a.alphabet = string.ascii_lowercase
print(a.alphabet)Tecnologia
No código acima, criamos uma classe chamada Alphabet sem atributos, mas informamos ao Python que queríamos colocar em maiúsculas qualquer atributo definido na instância da classe.
Sabemos que POO pode parecer distante para alguns leitores. Para ficar em dia com POO em Python, confira a Python Programmer Track da DataCamp.
Métodos mágicos matemáticos
Outra categoria de magic methods do Python são os métodos matemáticos (também conhecidos como operadores aritméticos). Expressões são criadas usando símbolos especiais chamados ‘operadores’ no Python. Usar esses operadores em uma expressão envolve criar objetos com magic methods matemáticos. Se isso não for atendido, será levantado um TypeError.
classNumbers:def__init__(self, a, b):
self.a = a
self.b = b
set_a = Numbers(2,4)
set_b = Numbers(3,5)
print(set_a + set_b)"""
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/tmp/ipykernel_152/3639634723.py in <module>
7 set_b = Numbers(3, 5)
8
----> 9 print(set_a + set_b)
TypeError: unsupported operand type(s) for +: 'Numbers' and 'Numbers'
"""Tecnologia
No código acima, tentamos criar múltiplas instâncias da classe Numbers e somá-las, o que gerou um erro como esperado. Uma solução seria criar um método na nossa classe para somar números, mas isso impediria a criação de expressões.
Uma solução melhor é implementar o magic method __add__ assim:
# Usando o operador de somaclassNumbers:def__init__(self, a, b):
self.a = a
self.b = b
def__add__(self, other):# Permitir apenas a soma de objetos Numbersifnotisinstance(other, Numbers):return NotImplemented
return Numbers(other.a + self.a, other.b + self.b)def__repr__(self):returnf"{self.__class__.__qualname__}({self.a}, {self.b})"
a = Numbers(2,4)
b = Numbers(3,5)print(a + b)# Deve resultar em Numbers(5, 9)"""
RandomNumbers(5, 9)
"""Tecnologia
O exemplo acima só permite somar dois objetos Numbers. Vamos implementar outro método matemático que permita multiplicar um objeto Numbers por um inteiro.
# Multiplicando Numbers por um inteiroclassNumbers:def__init__(self, a, b):
self.a = a
self.b = b
def__add__(self, other):# Permitir apenas a soma de objetos Numbersifnotisinstance(other, Numbers):return NotImplemented
return Numbers(other.a + self.a, other.b + self.b)def__mul__(self, other):ifnotisinstance(other,int):return NotImplemented
return Numbers(self.a * other, self.b * other)def__repr__(self):returnf"{self.__class__.__qualname__}({self.a}, {self.b})"
a = Numbers(2,4)print(a *5)# Deve retornar 10, 20"""
RandomNumbers(10, 20)
"""Tecnologia
No código acima, adicionamos o magic method __mul__ para permitir multiplicar os números da nossa classe personalizada por um inteiro.
Perceba que o objeto Numbers aparece no lado esquerdo do operador *. Isso não é por acaso. Vamos colocá-lo no lado direito do operando e ver o que acontece.
- snip --
a = Numbers(2,4)print(3* a)---------------------------------------------------------------------------
TypeError Traceback (most recent call last)/tmp/ipykernel_152/2857655049.py in<module>1 a = Numbers(2,4)---->2print(3* a)
TypeError: unsupported operand type(s)for*:'int'and'Numbers'Tecnologia
O Python lança um TypeError.
O __mul__ que implementamos só trata o caso em que o objeto Numbers está à esquerda do operador *. Para estender essa funcionalidade, precisamos adicionar o método reverso, __rmul__.
# Habilitando multiplicação reversaclassNumbers:def__init__(self, a, b):
self.a = a
self.b = b
def__add__(self, other):# Permitir apenas a soma de objetos Numbersifnotisinstance(other, Numbers):return NotImplemented
return Numbers(other.a + self.a, other.b + self.b)def__mul__(self, other):ifnotisinstance(other,int):return NotImplemented
return Numbers(self.a * other, self.b * other)def__rmul__(self, other):return self.__mul__(other)def__repr__(self):returnf"{self.__class__.__qualname__}({self.a}, {self.b})"
a = Numbers(2,4)print(5* a)# Deve retornar 10, 20"""
Numbers(10, 20)
"""Tecnologia
Agora podemos criar uma expressão de multiplicação sem nos preocupar com o lado do operando em que o objeto Numbers aparece.
Conclusão
O princípio básico para aproveitar os magic methods do Python é o mesmo: criar uma classe e sobrescrever os magic methods de object. Existem muitos magic methods em Python, o que tornaria este um tutorial enorme se fôssemos cobrir todos individualmente. Recomendamos fortemente consultar a documentação do Python para ter uma visão completa dos magic methods que você pode usar para aprimorar suas habilidades de programação em Python.
Descubra como escrever funções em Python reutilizáveis e eficientes. Domine parâmetros, instruções de retorno e temas avançados como funções lambda. Organize melhor seu código com main() e outras boas práticas.
Um dos fatores que tornam o Python uma linguagem de programação tão poderosa são os magic methods. Em alguns círculos de Pythonistas, você pode ouvi-los sendo chamados de ‘dunder methods’, por causa do padrão de dois underlines.
O papel deles no Python é realizar um procedimento conhecido como ‘sobrecarga de operadores’ (operator overloading), que basicamente dá um significado estendido a um operador predefinido. Em outras palavras, usamos a sobrecarga de operadores para adicionar comportamentos personalizados às nossas classes e habilitar seu uso com os operadores e funcionalidades nativas do Python.
Neste artigo, vamos nos aprofundar nos magic methods do Python, abordando:
Confira este caderno no DataLab para acompanhar o código usado neste tutorial.
Métodos vs. funções em Python
Antes de explicar o que são magic methods, precisamos entender as diferenças entre método e função em Python.
O Python usa métodos para representar o comportamento de um objeto. Assim, métodos estão associados a um objeto e executam operações sobre os dados dentro desse objeto. Outro ponto distintivo é o parâmetro self, que deve sempre aparecer como o primeiro parâmetro passado ao método.
Observação: não precisa ser literalmente a palavra self como primeiro parâmetro, mas a comunidade Pythonista concorda que é a melhor prática.
Métodos dependem da classe à qual estão associados. Portanto, para chamar um método, é preciso criar uma instância da classe antes de acessá-lo com o operador de ponto.
Uma função, por outro lado, é diferente: não precisamos criar uma instância de classe.
Funções em Python são passos executados como um bloco de código apenas quando chamadas. Elas são definidas de forma independente de qualquer objeto de classe, o que significa que podem ser chamadas diretamente pelo nome. Como não estão associadas a nenhuma classe, não precisamos passar self como parâmetro – parâmetros são opcionais.
Em essência, uma função é simplesmente um conjunto de instruções bem definidas que o interpretador usa para executar uma tarefa.
Como exploramos no artigo Como chamar e escrever funções, funções também vêm em dois tipos: nativas e definidas pelo usuário.
Em muitos aspectos, métodos e funções são idênticos – ambos são definidos com a instrução def. A diferença é que métodos estão associados a um objeto e operam sobre os dados contidos em uma classe, enquanto funções são definidas de forma independente e podem ser chamadas pelo nome em qualquer lugar do programa.
O que são os magic methods do Python?
No início do tutorial, mencionamos que magic methods também são chamados de dunder methods por conta do padrão de dois underlines. Isso quer dizer que todos os magic methods em Python começam e terminam com dois underlines.
O magic method mais conhecido é o __init__, que permite inicializar uma classe com atributos específicos.
Para instanciar essa classe, precisamos especificar os atributos name, age e role da instância. Por exemplo:
Se não fizermos isso, será levantado um TypeError.
O método __init__ faz parte da categoria de magic methods de inicialização e construção, mas é só um entre vários. A tabela abaixo mostra outros métodos dessa categoria.
__new__(cls, other)
Usado para criar uma nova instância de uma classe
__init__(self, other)
Chamado depois que uma nova instância é criada; todos os atributos passados ao construtor da classe serão usados como atributos.
__del__(self)
Também conhecido como destrutor; chamado quando uma instância vai ser destruída.
A forma mais simples de entender os magic methods é como um contrato entre a sua implementação e o interpretador do Python. Uma das cláusulas exige que o Python realize certas ações nos bastidores a partir de determinadas situações, como inicializar uma instância de classe com objetos específicos.
Em outras palavras, não é sua responsabilidade invocar um magic method diretamente – o Python faz isso por você, por baixo do capô.
Métodos de representação em string
Python é uma linguagem de programação orientada a objetos (POO) – tudo em Python é um objeto. Quando um novo objeto é criado, implicitamente criamos um objeto relacionado porque todas as classes herdam de object.
Observação: você também pode tornar a herança explícita passando a classe object para a sua classe.
No código acima, criamos duas classes vazias, mas o interpretador ainda soube o que imprimir quando chamamos print. Isso é possível porque todas as classes herdam da classe object (a mãe de todas as classes no Python), que possui um magic method chamado __repr__, o qual o print chama para retornar um valor ao nosso programa principal.
Quem já conhece herança sabe que uma classe filha pode sobrescrever sua classe pai definindo seu próprio método com o mesmo nome.
Você também pode usar o magic method __str__ para criar uma representação em string que é chamada pelo print.
Na verdade, o __repr__ funciona como comportamento de fallback do __str__. Assim, quando chamamos print, o Python primeiro procura o método __str__ definido na sua classe e, se não encontrar, recorre ao __repr__.
A tabela abaixo lista outros magic methods de representação em string.
__str__(self)
Chamado pela função nativa str() do Python e pelo print para exibir a representação informal em string de um objeto. Observe que não precisa ser uma expressão Python válida.
__repr__(self)
Chamado pela função nativa repr() do Python e por conversões de string para exibir a representação oficial em string de um objeto. Idealmente, deve parecer uma expressão Python válida, mas, se não for possível, uma string com uma descrição útil é preferida.
__unicode__(self)
Chamado para implementar a função nativa unicode() do Python e retorna um objeto Unicode.
__hash__(self)
Chamado pela função nativa hash() do Python e por operações em membros de coleções com hash. O método deve retornar um inteiro.
__nonzero__(self)
Chamado pela operação nativa bool() do Python para implementar testes de verdade. O método deve retornar True ou False (ou seus equivalentes inteiros 1 ou 0).
Métodos "mágicos" de encapsulamento
Encapsulamento é um dos conceitos fundamentais da programação orientada a objetos (POO). Ele se refere a agrupar dados com os métodos que operam sobre esses dados ou restringir o acesso direto a certos componentes dentro de um objeto.
Desenvolvedores vindos de outras linguagens de POO costumam criticar o fato de o Python não adotar encapsulamento de forma tão explícita nas classes. Eles estão acostumados a modificadores para métodos ou campos que permitem definir, por exemplo, atributos privados com getters e setters públicos. No Python, boa parte do encapsulamento acontece via magic methods.
Por exemplo, __setattr__ é uma solução de encapsulamento que nos permite definir o comportamento da atribuição a um atributo, exista ele previamente ou não. Em outras palavras, é possível definir regras para mudanças nos valores dos atributos.
No código acima, criamos uma classe chamada Alphabet sem atributos, mas informamos ao Python que queríamos colocar em maiúsculas qualquer atributo definido na instância da classe.
Sabemos que POO pode parecer distante para alguns leitores. Para ficar em dia com POO em Python, confira a Python Programmer Track da DataCamp.
Métodos mágicos matemáticos
Outra categoria de magic methods do Python são os métodos matemáticos (também conhecidos como operadores aritméticos). Expressões são criadas usando símbolos especiais chamados ‘operadores’ no Python. Usar esses operadores em uma expressão envolve criar objetos com magic methods matemáticos. Se isso não for atendido, será levantado um TypeError.
No código acima, tentamos criar múltiplas instâncias da classe Numbers e somá-las, o que gerou um erro como esperado. Uma solução seria criar um método na nossa classe para somar números, mas isso impediria a criação de expressões.
Uma solução melhor é implementar o magic method __add__ assim:
O exemplo acima só permite somar dois objetos Numbers. Vamos implementar outro método matemático que permita multiplicar um objeto Numbers por um inteiro.
No código acima, adicionamos o magic method __mul__ para permitir multiplicar os números da nossa classe personalizada por um inteiro.
Perceba que o objeto Numbers aparece no lado esquerdo do operador *. Isso não é por acaso. Vamos colocá-lo no lado direito do operando e ver o que acontece.
O Python lança um TypeError.
O __mul__ que implementamos só trata o caso em que o objeto Numbers está à esquerda do operador *. Para estender essa funcionalidade, precisamos adicionar o método reverso, __rmul__.
Agora podemos criar uma expressão de multiplicação sem nos preocupar com o lado do operando em que o objeto Numbers aparece.
Conclusão
O princípio básico para aproveitar os magic methods do Python é o mesmo: criar uma classe e sobrescrever os magic methods de object. Existem muitos magic methods em Python, o que tornaria este um tutorial enorme se fôssemos cobrir todos individualmente. Recomendamos fortemente consultar a documentação do Python para ter uma visão completa dos magic methods que você pode usar para aprimorar suas habilidades de programação em Python.