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?

313
demandé sur dreftymac 2010-07-19 06:11:19

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__ .

371
répondu Ned Batchelder 2010-07-19 02:22:00

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)
79
répondu N Randhawa 2017-08-13 20:51:26

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.

15
répondu Sam Dolan 2017-05-23 12:02:44

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

13
répondu Simon K Bhatta4ya 2018-06-20 07:34:08

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

6
répondu Mr Fooz 2010-07-19 02:18:09