Différence entre getattr et getattribute
j'essaie de comprendre quand utiliser __getattr__
ou __getattribute__
. La "documentation 151940920 mentionne __getattribute__
s'applique aux classes de nouveau style. Quels sont les nouveaux-classes de style?
5 réponses
une différence clé entre __getattr__
et __getattribute__
est que __getattr__
n'est invoqué que si l'attribut n'a pas été trouvé de la manière habituelle. Il est bon pour mettre en œuvre une solution de rechange pour les attributs manquants, et est probablement celui de deux que vous voulez.
__getattribute__
est invoqué avant de regarder les attributs réels sur l'objet, et peut donc être difficile à mettre en œuvre correctement. Vous pouvez finir en récursions infinies très facilement.
Les classes New-style dérivent de object
, les classes old-style sont celles de Python 2.x Sans classe de base explicite. Mais la distinction entre les anciennes et les nouvelles classes n'est pas importante lorsque l'on choisit entre __getattr__
et __getattribute__
.
Vous voudrez certainement __getattr__
.
montre quelques exemples simples de __getattr__
et __getattribute__
méthodes magiques.
__getattr__
Python appellera la méthode __getattr__
chaque fois que vous demandez un attribut qui n'a pas encore été défini. Dans l'exemple suivant, ma classe Count n'a pas de méthode __getattr__
. Maintenant lorsque j'essaie d'accéder à la fois à obj1.mymin
et obj1.mymax
attributs tout fonctionne bien. Mais quand j'essaie de accès obj1.mycurrent
attribut -- Python me donne AttributeError: 'Count' object has no attribute 'mycurrent'
class Count():
def __init__(self,mymin,mymax):
self.mymin=mymin
self.mymax=mymax
obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.mycurrent) --> AttributeError: 'Count' object has no attribute 'mycurrent'
Maintenant, ma classe Count a __getattr__
la méthode. Maintenant, quand j'essaie d'accéder à l'attribut obj1.mycurrent
-- python me renvoie tout ce que j'ai implémenté dans ma méthode __getattr__
. Dans mon exemple, chaque fois que j'essaie d'appeler un attribut qui n'existe pas, python crée cet attribut et le définit à la valeur entière 0.
class Count:
def __init__(self,mymin,mymax):
self.mymin=mymin
self.mymax=mymax
def __getattr__(self, item):
self.__dict__[item]=0
return 0
obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.mycurrent1)
__getattribute__
permet maintenant de voir la méthode __getattribute__
. Si vous avez la méthode __getattribute__
dans votre classe, python invoque cette méthode pour chaque attribut qu'il existe ou non. Alors pourquoi avons-nous besoin de la méthode __getattribute__
? Une bonne raison est que vous pouvez empêcher l'accès aux attributs et les rendre plus sûrs comme le montre l'exemple suivant.
chaque fois que quelqu'un essaie d'accéder à mes attributs qui commence par la soustraction 'cur' python soulève AttributeError
l'exception". Sinon, il renvoie cet attribut.
class Count:
def __init__(self,mymin,mymax):
self.mymin=mymin
self.mymax=mymax
self.current=None
def __getattribute__(self, item):
if item.startswith('cur'):
raise AttributeError
return object.__getattribute__(self,item)
# or you can use ---return super().__getattribute__(item)
obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.current)
Important: afin d'éviter la récursion infinie dans la méthode __getattribute__
, son implémentation devrait toujours appeler la méthode de classe de base avec le même nom pour accéder à tous les attributs dont elle a besoin. Par exemple: object.__getattribute__(self, name)
ou super().__getattribute__(item)
et non self.__dict__[item]
IMPORTANT
si votre classe contient à la fois getattr et getattribute méthodes magiques puis __getattribute__
est appelé en premier. Mais si __getattribute__
augmente
Exception AttributeError
alors l'exception sera ignorée et la méthode __getattr__
sera invoquée. Voir l'exemple suivant:
class Count(object):
def __init__(self,mymin,mymax):
self.mymin=mymin
self.mymax=mymax
self.current=None
def __getattr__(self, item):
self.__dict__[item]=0
return 0
def __getattribute__(self, item):
if item.startswith('cur'):
raise AttributeError
return object.__getattribute__(self,item)
# or you can use ---return super().__getattribute__(item)
# note this class subclass object
obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.current)
Nouveau style de classes héritent de object
, ou d'une autre nouvelle classe de style:
class SomeObject(object):
pass
class SubObject(SomeObject):
pass
Vieux style de classes n'est pas:
class SomeObject:
pass
cela ne s'applique qu'à Python 2 - en Python 3 Tout ce qui précède créera des classes de nouveau style.
voir 9. Classes (tutoriel Python), NewClassVsClassicClass et What is la différence entre les anciennes et les nouvelles classes de style en Python? pour plus de détails.
ce n'est qu'un exemple basé sur l'explication de Ned Batchelder .
__getattr__
exemple:
class Foo(object):
def __getattr__(self, attr):
print "looking up", attr
value = 42
self.__dict__[attr] = value
return value
f = Foo()
print f.x
#output >>> looking up x 42
f.x = 3
print f.x
#output >>> 3
print ('__getattr__ sets a default value if undefeined OR __getattr__ to define how to handle attributes that are not found')
et si le même exemple est utilisé avec __getattribute__
vous obtiendriez > > > RuntimeError: maximum recursion depth exceeded while calling a Python object
les classes de nouveau style sont celles qui font partie de la sous-classe "objet" (directement ou indirectement). Ils ont une méthode de classe __new__
en plus de __init__
et ont un comportement de bas niveau un peu plus rationnel.
habituellement, vous aurez envie de passer outre __getattr__
(si vous êtes en train de passer outre l'un ou l'autre), sinon vous aurez du mal à supporter " self.syntaxe foo " dans vos méthodes.
info supplémentaire: http://www.devx.com/opensource/Article/31482/0/page/4