Estou tentando entender quando usar __getattr__
ou __getattribute__
. A documentação mencionada __getattribute__
se aplica a classes de novo estilo. O que são classes de novo estilo?
python
getattr
getattribute
Yarin
fonte
fonte
Respostas:
Uma diferença importante entre
__getattr__
e__getattribute__
é que__getattr__
só é invocada se o atributo não foi encontrado da maneira usual. É bom para implementar um fallback para atributos ausentes e provavelmente é o dois que você deseja.__getattribute__
é invocado antes de examinar os atributos reais no objeto e, portanto, pode ser difícil de implementar corretamente. Você pode terminar em recursões infinitas com muita facilidade.Classes de estilo novo derivam
object
, classes de estilo antigo são aquelas no Python 2.x sem classe base explícita. Mas a distinção entre classes de estilo antigo e novo não é importante ao escolher entre__getattr__
e__getattribute__
.Você quase certamente quer
__getattr__
.fonte
__getattribute__
será chamado para todo acesso e__getattr__
será chamado para os tempos que__getattribute__
aumentaram umAttributeError
. Por que não apenas manter tudo em um?__getattribute__
.objec.__getattribute__
invocamyclass.__getattr__
nas circunstâncias certas.Vamos ver alguns exemplos simples de métodos
__getattr__
e__getattribute__
métodos mágicos.__getattr__
O Python chamará o
__getattr__
método sempre que você solicitar um atributo que ainda não tenha sido definido. No exemplo a seguir, minha classe Count não tem__getattr__
método. Agora, principalmente, quando tento acessar os doisobj1.mymin
e osobj1.mymax
atributos, tudo funciona bem. Mas quando eu tento acessarobj1.mycurrent
atributo - Python me dáAttributeError: 'Count' object has no attribute 'mycurrent'
Agora minha classe Count tem
__getattr__
método. Agora, quando tento acessar oobj1.mycurrent
atributo - python, retorna-me o que implementamos no meu__getattr__
método. No meu exemplo, sempre que tento chamar um atributo que não existe, o python cria esse atributo e o define como valor inteiro 0.__getattribute__
Agora vamos ver o
__getattribute__
método. Se você possui um__getattribute__
método em sua classe, o python chama esse método para cada atributo, independentemente de existir ou não. Então, por que precisamos de__getattribute__
método? Um bom motivo é que você pode impedir o acesso aos atributos e torná-los mais seguros, conforme mostrado no exemplo a seguir.Sempre que alguém tenta acessar meus atributos que começam com a substring 'cur' python, gera
AttributeError
exceção. Caso contrário, ele retornará esse atributo.Importante: Para evitar recursões infinitas no
__getattribute__
método, sua implementação sempre deve chamar o método da classe base com o mesmo nome para acessar todos os atributos necessários. Por exemplo:object.__getattribute__(self, name)
ousuper().__getattribute__(item)
e nãoself.__dict__[item]
IMPORTANTE
Se sua classe contêm ambos GetAttr e getAttribute métodos mágicos, em seguida,
__getattribute__
é chamado pela primeira vez. Mas se__getattribute__
geraAttributeError
exceção, a exceção será ignorada e o__getattr__
método será invocado. Veja o seguinte exemplo:fonte
__getattribute__
mas certamente isso não é. Porque, por seu exemplo, tudo o que você está fazendo__getattribute__
é gerar umaAttributeError
exceção se o atributo não estiver presente no__dict__
objeto; mas você realmente não precisa disso porque essa é a implementação padrão__getattribute__
e o fato__getattr__
é exatamente o que você precisa como mecanismo de fallback.current
é definido em instânciasCount
(ver__init__
), então simplesmente levantarAttributeError
se o atributo não está lá não é bem o que está acontecendo - que adia para__getattr__
para todos os nomes que começam 'cur', incluindocurrent
, mas tambémcurious
,curly
...Este é apenas um exemplo baseado na explicação de Ned Batchelder .
__getattr__
exemplo:E se o mesmo exemplo for usado com
__getattribute__
Você obterá >>>RuntimeError: maximum recursion depth exceeded while calling a Python object
fonte
__getattr__()
implementações do mundo real aceitam apenas um conjunto finito de nomes de atributos válidos aumentandoAttributeError
para nomes de atributos inválidos, evitando problemas sutis e difíceis de depurar . Este exemplo aceita incondicionalmente todos os nomes de atributos como válidos - um uso bizarro (e francamente propenso a erros)__getattr__()
. Se você deseja "controle total" sobre a criação de atributos, como neste exemplo, você deseja__getattribute__()
.defaultdict
.__getattr__
será chamado antes da pesquisa da superclasse. Isso é bom para uma subclasse direta deobject
, já que os únicos métodos com os quais você realmente se importa são os métodos mágicos que ignoram a instância, mas para qualquer estrutura de herança mais complexa, você remove completamente a capacidade de herdar qualquer coisa do pai.As novas classes de estilo são herdadas
object
ou de outra nova classe de estilo:As classes de estilo antigo não:
Isso se aplica apenas ao Python 2 - no Python 3, todas as opções acima criarão classes de novo estilo.
Consulte 9. Classes (tutorial em Python), NewClassVsClassicClass e Qual é a diferença entre o estilo antigo e as novas classes de estilo no Python? para detalhes.
fonte
As classes de novo estilo são aquelas que subclassificam "objeto" (direta ou indiretamente). Eles têm um
__new__
método de classe além__init__
e têm um comportamento de baixo nível um pouco mais racional.Geralmente, você deseja substituir
__getattr__
(se estiver substituindo), caso contrário, será difícil dar suporte à sintaxe "self.foo" em seus métodos.Informações adicionais: http://www.devx.com/opensource/Article/31482/0/page/4
fonte
Ao ler o Beazley & Jones PCB, deparei-me com um caso de uso explícito e prático,
__getattr__
que ajuda a responder à parte "quando" da pergunta do OP. Do livro:"O
__getattr__()
método é como um exemplo geral para a pesquisa de atributos. É um método que é chamado se o código tentar acessar um atributo que não existe". Sabemos disso pelas respostas acima, mas na receita PCB 8.15, essa funcionalidade é usada para implementar o padrão de design da delegação . Se o Objeto A tiver um atributo Objeto B que implemente muitos métodos que o Objeto A deseja delegar, em vez de redefinir todos os métodos do Objeto B no Objeto A apenas para chamar os métodos do Objeto B, defina um__getattr__()
método da seguinte maneira:onde _b é o nome do atributo do Objeto A que é um Objeto B. Quando um método definido no Objeto B é chamado no Objeto A, o
__getattr__
método será chamado no final da cadeia de pesquisa. Isso tornaria o código mais limpo também, já que você não possui uma lista de métodos definidos apenas para delegar a outro objeto.fonte