Tenho brincado com Python recentemente e uma coisa que estou achando um pouco estranho é o uso extensivo de 'métodos mágicos', por exemplo, para tornar seu comprimento disponível, um objeto implementa um método def __len__(self)
, e então é chamado quando você escrevelen(obj)
.
Eu estava me perguntando por que os objetos simplesmente não definem um len(self)
método e o chamam diretamente como um membro do objeto, por exemplo obj.len()
? Tenho certeza de que deve haver bons motivos para o Python agir dessa maneira, mas, como um novato, ainda não descobri quais são.
python
magic-methods
Greg Beech
fonte
fonte
len()
oureversed()
se aplica a muitos tipos de objetos, mas um método comoappend()
se aplica apenas a sequências, etc.Respostas:
AFAIK
len
é especial a este respeito e tem raízes históricas.Aqui está uma citação do FAQ :
Os outros "métodos mágicos" (na verdade chamados de método especial no folclore Python) fazem muito sentido e existem funcionalidades semelhantes em outras linguagens. Eles são usados principalmente para código que é chamado implicitamente quando a sintaxe especial é usada.
Por exemplo:
e assim por diante...
fonte
Do Zen do Python:
Esta é uma das razões - com métodos personalizados, os desenvolvedores estaria livre para escolher um nome método diferente, como
getLength()
,length()
,getlength()
ou qualquer. Python impõe nomenclatura estrita para que a função comumlen()
possa ser usada.Todas as operações comuns a muitos tipos de objetos são colocadas em métodos mágicos, como
__nonzero__
,__len__
ou__repr__
. Eles são principalmente opcionais, no entanto.A sobrecarga de operador também é feita com métodos mágicos (por exemplo
__le__
), portanto, faz sentido usá-los para outras operações comuns também.fonte
Python usa a palavra "métodos mágicos" , porque esses métodos realmente fazem mágica para seu programa. Uma das maiores vantagens de usar os métodos mágicos do Python é que eles fornecem uma maneira simples de fazer os objetos se comportarem como tipos embutidos. Isso significa que você pode evitar maneiras feias, contra-intuitivas e não padronizadas de realizar operações básicas.
Considere o seguinte exemplo:
Isso dá um erro, porque o tipo de dicionário não suporta adição. Agora, vamos estender a classe de dicionário e adicionar o método mágico "__add__" :
Agora, ele fornece a seguinte saída.
Assim, ao adicionar este método, de repente a mágica aconteceu e o erro que você estava obtendo antes desapareceu.
Espero que isso deixe as coisas claras para você. Para obter mais informações, consulte:
A Guide to Python's Magic Methods (Rafe Kettler, 2012)
fonte
Algumas dessas funções fazem mais do que um único método seria capaz de implementar (sem métodos abstratos em uma superclasse). Por exemplo,
bool()
age mais ou menos assim:Você também pode ter 100% de certeza de que
bool()
sempre retornará True ou False; se você confiasse em um método, não poderia ter certeza do que receberia de volta.Algumas outras funções que têm implementações relativamente complicadas (mais complicadas do que os métodos mágicos subjacentes provavelmente são) são
iter()
ecmp()
, e todos os métodos de atributo (getattr
,setattr
edelattr
). Coisas comoint
também acessar métodos mágicos ao fazer coerção (você pode implementar__int__
), mas têm dupla função como tipos.len(obj)
é na verdade o único caso em que não acredito que seja diferenteobj.__len__()
.fonte
hasattr()
eu usariatry:
/except AttributeError:
e em vez de,if obj.__len__(): return True else: return False
eu apenas diria,return obj.__len__() > 0
mas essas são apenas coisas estilísticas.bool(x)
referex.__nonzero__()
), seu método não funcionaria. As instâncias de bool têm um método__nonzero__()
e seu código continuaria chamando a si mesmo assim que obj fosse um bool. Talvezbool(obj.__bool__())
deva ser tratado da mesma maneira que você tratou__len__
? (Ou este código realmente funciona para Python 3?)len(x)
ex.__len__()
é que o primeiro aumentará OverflowError para comprimentos que excedamsys.maxsize
, enquanto o último geralmente não para tipos implementados em Python. No entanto, isso é mais um bug do que um recurso (por exemplo, o objeto de intervalo do Python 3.2 pode lidar com intervalos arbitrariamente grandes, mas usarlen
com eles pode falhar.__len__
Porém, eles também falham, já que são implementados em C em vez de Python)Eles não são realmente "nomes mágicos". É apenas a interface que um objeto deve implementar para fornecer um determinado serviço. Nesse sentido, eles não são mais mágicos do que qualquer definição de interface predefinida que você precise reimplementar.
fonte
Embora o motivo seja principalmente histórico, existem algumas peculiaridades no Python
len
que tornam apropriado o uso de uma função em vez de um método.Algumas operações em Python são implementadas como métodos, por exemplo
list.index
edict.append
, enquanto outras são implementadas como chamáveis e métodos mágicos, por exemplostr
eiter
ereversed
. Os dois grupos diferem o suficiente para que a abordagem diferente seja justificada:str
,int
e amigos são tipos. Faz mais sentido chamar o construtor.iter
pode chamar__getitem__
se__iter__
não estiver disponível e oferece suporte a argumentos adicionais que não cabem em uma chamada de método. Pelo mesmo motivoit.next()
foi alterado paranext(it)
em versões recentes do Python - faz mais sentido.__iter__
e__next__
- é chamado defor
loop. Para consistência, uma função é melhor. E torna-o melhor para certas otimizações.repr
atuam como ostr
faz. Terstr(x)
contrax.repr()
seria confuso.isinstance
.getattr(x, 'a')
é outra forma de fazerx.a
egetattr
compartilha muitas das qualidades acima mencionadas.Eu pessoalmente chamo o primeiro grupo de método e o segundo grupo de operador. Não é uma distinção muito boa, mas espero que ajude de alguma forma.
Dito isso,
len
não se encaixa exatamente no segundo grupo. Está mais próximo das operações do primeiro, com a única diferença de que é muito mais comum do que quase qualquer uma delas. Mas a única coisa que faz é ligar__len__
, e está muito perto deL.index
. No entanto, existem algumas diferenças. Por exemplo,__len__
pode ser chamado para a implementação de outros recursos, comobool
, se o método foi chamado,len
você pode quebrarbool(x)
com olen
método personalizado que faz coisas completamente diferentes.Em suma, você tem um conjunto de recursos muito comuns que as classes podem implementar e que podem ser acessados por meio de um operador, por meio de uma função especial (que geralmente faz mais do que a implementação, como um operador faria), durante a construção do objeto, e todos eles compartilham alguns traços comuns. Todo o resto é um método. E
len
é uma espécie de exceção a essa regra.fonte
Não há muito a acrescentar às duas postagens acima, mas todas as funções "mágicas" não são realmente mágicas. Eles fazem parte do módulo __ builtins__ que é importado implícita / automaticamente quando o interpretador é iniciado. Ie:
acontece sempre antes do início do programa.
Sempre achei que seria mais correto se o Python fizesse isso apenas para o shell interativo e exigisse scripts para importar as várias partes internas de que precisavam. Provavelmente, um manuseio diferente de __ main__ seria bom em shells vs interativo. Enfim, confira todas as funções e veja como é sem elas:
fonte