Advanced Python: O que são métodos mágicos?

Este artigo destaca os métodos especiais de Python que todos os programadores Python devem conhecer

Farhad Malik

Follow

> 16 de Maio, 2020 – 14 min ler

>

>
>

>

>

>

>

>

>>

Os métodos mágicos ajudam-nos a enriquecer as nossas aplicações. Eles indiretamente adicionam magia ao nosso código Python. Este é um tópico de nível avançado para desenvolvedores Python e eu o recomendo para todos que estão/ou pretendem usar a linguagem de programação Python.

Os métodos mágicos nos dão mais controle sobre como nossa aplicação se comporta.

Este artigo tem como objetivo explicar o que são métodos mágicos e como eles podem ser usados para construir aplicações Python. Ele irá fornecer uma visão geral dos métodos mágicos mais amplamente utilizados para uma série de tipos de dados.

O propósito de delinear os métodos mágicos chave é para que possamos entender se queremos sobrepor esses métodos em nossas classes personalizadas para enriquecer as aplicações.

Métodos mágicos tornam a linguagem de programação Python extremamente poderosa

>

Foto por Rodion Kutsaev em Unsplash

O que são os métodos mágicos Python?

Os métodos mágicos Python também são conhecidos como métodos especiais ou métodos dunder. Eles são rodeados por sublinhados duplos, por exemplo __init__().

Um objeto pode ter vários métodos mágicos.

Remmbrar tudo em Python é um objeto incluindo uma variável/função/classe, etc. Os objetos são a abstração de python para dados.

Os métodos mágicos são usados para construir e rubricar novos objetos, eles nos ajudam a recuperar um objeto como um dicionário, eles são usados para apagar um objeto entre outras operações. Eles são usados quando o operador + é invocado, ou mesmo quando queremos representar um objeto como uma string.

Embora todos os métodos em Python sejam públicos, a convenção de codificação é cercar todos os métodos privados através de duplo sublinhado __<método>__()

Isto implica que os métodos mágicos são destinados a ser métodos privados. Isso também significa que o chamador de um objeto não deve invocar o método diretamente, pois o método se destina a ser invocado pela classe interna que tem o método mágico.

Podemos sobrepor os métodos mágicos para fornecer nossa própria funcionalidade personalizada.

>

Foto por Cristian Escobar em Unsplash

Explicarei os conceitos de métodos mágicos criando uma classe personalizada e depois darei uma visão geral dos métodos mágicos mais importantes em um inteiro, string, lista e um dicionário.

A medida que avançamos no artigo, ele começará a construir uma imagem muito mais clara do porquê da existência dos métodos mágicos e de como usá-los.

Se você quiser entender a linguagem de programação Python desde o iniciante até um nível avançado, então eu recomendo fortemente o artigo abaixo:

I iniciará o tópico de métodos mágicos criando uma classe personalizada e então eu explicarei como os métodos mágicos são usados.

The Usecase To Understand Magic Methods

No trecho abaixo, eu criei uma classe chamada Human e então instanciei uma instância da classe Human.

Este trecho de código será usado para nos ajudar a entender métodos mágicos.

Nota, o id do objeto humano é do tipo inteiro, o atributo nome é do tipo string, os endereços das propriedades são do tipo lista e os mapas de propriedades são do tipo dicionário.

Eu agrupei os métodos mágicos em várias seções de tipos de dados para facilitar a leitura. No entanto, os mesmos métodos mágicos são encontrados em diferentes tipos de dados.

Os métodos mágicos podem ser sobrepostos para enriquecer a funcionalidade e criar uma lógica personalizada que melhor se adapte às necessidades do negócio.

Class:

Deixe entender os métodos mágicos que estão associados ao objeto humano. Se eu executar o método dir(humano), ele irá listar todas as funções e nomes de atributos do objeto humano.

Existem no total 29 métodos/atributos que estão associados ao objeto humano. Entre eles, 26 são os métodos mágicos.

É um número bastante grande de métodos especiais. Estes métodos são herdados do tipo base da classe Humana. Portanto, eles são os métodos predefinidos que podemos usar/override para enriquecer as classes.

A próxima parte da seção irá explicar os métodos mágicos chave.

__delattr__

Este método é chamado quando tentamos excluir um atributo de uma classe.

Podemos sobrepor a funcionalidade implementando o método na classe Humano:

def __delattr__(self, item):
print('Deleting attribute ' + item)
return object.__delattr__(self, item)

Agora, sempre que tentarmos excluir um atributo, ele exibirá a mensagem: Deleting attribute

Há também o método __setattr__() para atribuir um valor a um atributo e __getattr__() para obter um valor do atributo.

Uma usecase de__delattr__() pode ser para evitar que determinados atributos de um objeto sejam excluídos ou quando queremos executar ações específicas quando um determinado atributo é excluído.

__dict__

Este método retorna um dicionário que representa o objeto. As chaves do dicionário são os atributos do objeto e os valores são os valores dos atributos.

Como uma instância:

human = Human(2, 'Malik').__dict__
print(human)

O código acima retorna:

{‘id’: 2, ‘name’: ‘Malik’, ‘addresses’: , ‘mapas’: {}}

__dir__

Podemos substituir o método dir() substituindo o método __dir__() na classe. Como instância, podemos remover os métodos internos do resultado que é retornado pelo método dir():

__eq__

Este método é chamado quando tentamos executar a operação ==. Vamos considerar que dois objetos humanos são iguais quando seu atributo id é igual mesmo que seu nome seja diferente.

Pode substituir o método __eq__() para alcançar esta funcionalidade:

def __eq__(self, other):
return self.id == other.id

Este agora retornará True:

first = Human(1, 'Farhad')
second = Human(1, 'Malik')
print(first == second)

__format__

Quando tentamos fazer string.format(), o método __formato__() é invocado internamente.

__ge__

Como instância, vamos assumir que existem dois objetos humanos:

first = Human(1, 'Farhad')
second = Human(2, 'Malik')

Consideremos também que nosso projeto tem uma regra que um objeto humano com um Id maior é considerado maior do que o outro objeto humano. Portanto, podemos sobrepor o método __gt__() e implementar a lógica personalizada:

def __ge__(self, other):
return self.id >= other.id

Isso agora retornará Falso porque o id do segundo objeto humano é maior que o primeiro objeto humano:

print(first >= second)
Returns False

Existe também um método __lt__() que é executado quando o operador ≤ é executado.

__hash__

Hashing é usado para converter um objeto em um número inteiro. Hashing é executado quando tentamos definir um item em um dicionário/set.

Um bom algoritmo de hashing resulta em um número menor de colisões de hashing. Nós podemos fornecer nosso próprio algoritmo de hash substituindo o método __hash__().

Consideremos que o id do objeto Humano é suposto ser único em nossa aplicação. O algoritmo __hash__() pode ser sobreposto para retornar o self.id como o inteiro do hash:

def __hash__(self):
return self.id

Podemos criar dois objetos e salvá-los em uma coleção definida. Quando consultamos o comprimento do conjunto, esperamos dois elementos no conjunto porque ambos os objetos têm um id.

first = Human(1, 'Farhad')
second = Human(2, 'Malik')
my_set = set()
print(len(my_set))
#returns 2

Se agora definirmos o id como sendo 1 para ambos os objetos e repetirmos o exercício então veremos apenas 1 elemento no conjunto porque ambos os objetos têm a mesma chave de hash que seu atributo id é o mesmo, mesmo que seu atributo nome seja diferente.

first = Human(1, 'Farhad')
second = Human(1, 'Malik')
my_set = set()
print(len(my_set))
#returns 1

__init__

O método __init__() é executado quando queremos instanciar uma nova instância de uma classe chamando seu construtor.

Como uma instância, quando tentamos executar:

human = Human(1, 'farhad')

Então o método __init__() foi executado.

Nós podemos sobrescrever a funcionalidade e passar nossos próprios argumentos e comportamento personalizados nele também.

Como uma instância, o método __init__() da classe Humano é:

def __init__(self, id, name, addresses=, maps={}):
self.id = id
self.name = name
self.addresses = addresses
self.maps = maps
>

Foto por Cederic X em Unsplash

__init_subclass__

Este é um dos usecases da metaclasse. Quando uma classe é subclassada e seu objeto é criado, então o método __init_subclass__() é chamado.

Essencialmente, o método informa ao pai que ele foi subclassado. Este gancho pode então rubricar todas as subclasses de uma determinada classe. Portanto, o método é usado para registrar subclasses e atribuir valores padrão aos atributos nas subclasses. Assim, ele nos permite personalizar a inicialização de subclasses.

Explicarei como as metaclasses funcionam no meu próximo artigo.

__novo__

Quando queremos instanciar/criar uma nova instância de uma classe então o método __novo__(cls) é executado.

Como uma instância, vamos considerar que queremos imprimir ‘Human criando…’ sempre que o construtor Human() for chamado.

Podemos sobrepor a funcionalidade do método __new__(cls) como mostrado abaixo:

def __new__(cls, *args, **kwargs):
print('Human creating...')
return object.__new__(cls)

Como resultado, Human creating… é impresso quando tentamos criar uma instância:

human = Human(1, 'Farhad', , {'London':2, 'UK':3})

__sizeof__

Este método é chamado quando executamos sys.getizeof(). Ele retorna o tamanho do objeto na memória, em bytes.

__str__

Imprime: id=1. name=Farhad.

A função __str__() deve tentar retornar uma representação amigável do objeto.

__weakref__

Este objeto __weakref__ retorna a lista de referências fracas para o objeto alvo. Essencialmente, ele ajuda a coleta de lixo a informar as referências fracas que o referente foi coletado. Portanto, ele impede que os objetos acessem os indicadores subjacentes.

>

Photo by Yousef Espanioly on Unsplash

Integer

Isto nos leva à próxima seção do artigo. Podemos ver que a propriedade id do objeto humano é do tipo int. Se então executarmos dir() na propriedade id e filtrarmos os métodos que estão rodeados de duplo sublinhado, veremos que existem no total 62 métodos mágicos.

print(len(list(filter(lambda x: x.startswith('__') and x.endswith('__'), dir(human1.id)))))

I irá explicar os métodos chave aqui:

__add__

Este método é chamado quando tentamos adicionar dois números.

Como uma instância:

human.id + 2 é o mesmo que human.id.__add__(2)

__

Este método é executado quando tentamos usar o operador & e.g.:

return self & another_value

__bool__

Este método é executado quando tentamos executar a verificação booleana em um objeto e.g.:

self != 123

__floordiv__

Este método é executado quando tentamos executar o //operador.

Foto por Johannes Plenio em Unsplash

__getnewargs__

Ocasionalmente, nós picamos objetos em Python. Pickling cria uma representação em byte-stream de um objeto na memória que pode ser salvo no disco se necessário.

O método __getnewargs__() informa o processo de pickling sobre como ele precisa carregar de volta o objeto em pickles para recriar o objeto alvo. Em particular, como o objeto precisa ser criado, passando nos argumentos para o novo método(). Portanto, o nome ‘get new args’.

__index__

Consequentemente, um objeto pode ser convertido em um inteiro executando o método __index__(). Podemos também sobrepor o método e fornecer nossa própria funcionalidade de como o índice precisa ser gerado.

__invert__

Este método é executado quando usamos o operador ~.

Como uma instância:

first = Human(1, 'Farhad')
print(~first.id)

É o mesmo que executar:

first = Human(1, 'Farhad')
print(first.id.__invert__())

__lshift__

Este método nos dá um deslocamento à esquerda de um valor, por exemplo, self << valor. Podemos sobrecarregar o operador << substituindo o método __lshift__().

Nota: __rshift__() é executado quando executamos o operador >>> operador.

__mod__

Este método é chamado quando usamos o operador %.

__neg__

Este método é executado quando usamos o operador negativo – por exemplo.

first.id — second.id

__subclasshook__

Este método pode ser substituído para personalizar o método issubclass(). Essencialmente, ele retorna True se uma classe for uma subclasse e False se não for. O método também retorna NotImplemented que permite que o algoritmo existente seja usado.

O método pode personalizar o resultado do método issubclass().

Foto por Patrick Selin em Unsplash

String

Isto nos leva à próxima seção do artigo.

Vemos que a propriedade do nome do objeto humano é do tipo string. Se depois executarmos dir(human.name) na propriedade e filtrarmos os métodos que estão rodeados de duplo sublinhado, vamos notar que existem no total 33 métodos mágicos.

print(len(list(filter(lambda x: x.startswith('__') and x.endswith('__'), dir(human1.name)))))

Explicarei os 4 métodos chave aqui, pois o resto do artigo já destacou a maioria dos métodos mágicos.

__contém__

Este método é executado quando tentamos verificar se um determinado caractere existe.

__len__

Este método retorna o comprimento da string. Ele é executado quando executamos o método len().

Se queremos contar apenas caracteres específicos para calcular o comprimento da string então o método __()__ pode ser sobrescrito para fornecer essa funcionalidade.

__repr__

Este método é executado quando queremos criar uma representação amigável ao desenvolvedor de um objeto.

Nota, a __repr__ é executada quando imprimimos(objeto) se não tivermos uma implementação __str__() em nossa classe.

def __repr__(self):
return f'id={self.id} ({type(self.id)}). name={self.name} ({type(self.name)})'print(Human(1, 'Farhad'))

Esta irá imprimir:

id=1 (<classe ‘int’>). name=Farhad (<classe ‘str’>)

O método __repr__() deve ser destinado a produzir a representação oficial de um objeto.

__iadd__

Occasionalmente, nós usamos um operador de adição junto com a atribuição e.g.

self.id += 1

Este é equivalente a self.id = self.id + 1

O método iadd() é executado quando executamos a adição com a atribuição.

Outras vezes, o método __ipow__() é executado quando **= é executado.

O método mágico __iand__() é para executar um bitwise AND com atribuição e __ior__() é chamado quando tentamos fazer != tal como: i != j

List

Isto nos leva à próxima seção do artigo. A propriedade de endereços do objeto humano é do tipo lista. Se então executarmos dir(human.addresses) na propriedade addresses e filtrarmos os métodos que estão rodeados de sublinhados duplos, vamos encontrar que existem no total 35 métodos mágicos.

print(len(list(filter(lambda x: x.startswith('__') and x.endswith('__'), dir(human1.addresses)))))

Explicarei os métodos chave aqui:

__reduce__

Quando um objeto é picleado, o método __reduce__() é executado para retornar um objeto que ajuda o pickle a entender como construí-lo de volta.

__reduce_ex__

O método __reduce_ex__() é preferido pelo pickle ao invés do método __reduce__().

O método __reduce_ex__() leva um argumento inteiro que é a versão do protocolo. Ele fornece compatibilidade retroativa para decapagem e é usado para construir o fluxo de bytes de picles para o objeto.

__reversed__

O método __reversed__() é executado quando tentamos inverter uma coleção na seqüência inversa.

É executado quando o método reverse(collection) ou collection.reverse() é chamado. Algumas vezes, decidimos alterar a funcionalidade do método reverse().

Alterando o método __reversed__(), podemos alcançar o resultado desejado.

Dicionário

Isto nos leva à quinta seção do artigo.

Dicionário é um dos principais tipos de construção em Python. Se executarmos dir(human.maps) e filtrarmos os métodos que estão rodeados de duplo underscores, vamos encontrar que existem no total 29 métodos mágicos.

print(len(list(filter(lambda x: x.startswith('__') and x.endswith('__'), dir(human1.maps)))))

Entre os 29 métodos mágicos, vou explicar os 4 métodos chave aqui:

__delitem__

Este método é executado quando apagamos um item e.g.

del dictionary

__getitem__

Este método é executado quando tentamos obter um item para uma chave:

item = dictionary

__setitem__

Este método é executado quando tentamos definir um item no dicionário:

dictionary = item

__iter__

Este método retorna um iterador para a coleção. Um iterador nos ajuda a iterar sobre uma coleção.

Podemos sobrepor como o iterador() é executado substituindo o método __iter__().

Pouco, uma nota em __call__()

E se quisermos tornar nosso objeto chamável? Vamos considerar que queríamos tornar o objeto humano em uma função callable human()?

O método__call__() nos permite tratar as classes como funções.

human = Human(1, 'Farhad')
human()

Possibilitamos essa funcionalidade fornecendo a implementação do método mágico __call__() em nossa classe Humana.

Isso irá imprimir:

Você tentou chamar
Argumentos: ()
Argumentos de Palavras-Chave: {}
id=1 (<classe ‘int’>). name=Farhad (<classe ‘str’>)
Call completed

Altrapassando o método __call__(), podemos agora implementar um decorador para retornar um objeto como uma função ou mesmo chamar aquelas bibliotecas que aceitam funções como argumentos, passando nos objetos reais.

Foto by Dollar Gill on Unsplash

Sumário

Este é um tópico de nível avançado para desenvolvedores Python e eu recomendo a todos que estão/ou pretendem usar a linguagem de programação Python.

Este artigo pretende explicar o que são métodos mágicos e como eles podem ser usados para construir aplicações Python. Ele forneceu uma visão geral dos métodos mágicos mais usados em uma classe customizada, inteiros, strings, listas e tipos de dados de dicionário.

>

Embora cada método em Python seja público, a convenção de codificação é cercar todos os métodos privados através de duplo sublinhado __<method>__()

Isto implica que os métodos mágicos têm a intenção de ser métodos privados. Isto também significa que o chamador de um objeto não deve invocar diretamente o método e o método é invocado pela classe interna que tem o método mágico. Os métodos mágicos nos permitem ter mais controle sobre como nossa aplicação se comporta.

Os métodos mágicos podem ser sobrescritos para enriquecer a funcionalidade e criar uma lógica personalizada que melhor se adapte às necessidades do negócio.

O propósito de delinear os métodos mágicos chave é entender se queremos sobrescrever esses métodos em nossas classes personalizadas para enriquecer as aplicações.

Deixe uma resposta

O seu endereço de email não será publicado.