3
votes

Méthode __getattr__ en python

Je rencontre ce code aujourd'hui, et cela semble déroutant

class ClassOne:
    def __init__(self,some_object):
        self.not_important_attribute=some_object   
class ClassTwo:
    def __init__(self,some_object):
        self.tmp=ClassOne(some_object)
    def __getattr__(self,attr):
        return getattr(self.tmp,attr)
a=ClassTwo('not_important_string')
print(getattr(a,'undefined_attribute',3))

Lorsque nous utilisons getattr à la dernière ligne, nous déclenchons le __getattr__ dans la Sous-classe , puis déléguez à la fonction getattr (self.tmp, attr) qui lèvera une exception si l'attribut n'est pas défini et qu'aucune valeur par défaut n'a été donnée. Mais comment la valeur 3 à la dernière ligne passe-t-elle toujours par tout le processus, et revient finalement à la fonction getattr (a, 'undefined_attribute', 3) ?. Parce que nous n'avions pas d'espace pour la valeur par défaut lorsque nous déléguons getattr (self.tmp, attr) , comment est-ce possible?


3 commentaires

Ouais, ce n'est pas un mécanisme d'héritage, je vais éditer ça


Si _ getattr_ lève AttributeError alors la fonction getattr renvoie la valeur par défaut si elle existe (dans votre cas, 3).


@AndreyBerenda Je ne suis pas sûr que ce soit correct, lorsque la méthode __getattr__ est appelée, elle retournera la valeur que nous l'avons définie à l'intérieur de la fonction, pas la valeur par défaut en dehors de la classe, vérifiez ceci


4 Réponses :


2
votes

Dans votre cas,

getattr(self.tmp,attr)

lève AttributeError et si getattr a un troisième argument (valeur par défaut), alors il renvoie la valeur par défaut au lieu de lever AttributeError


5 commentaires

"si getattr a un troisième argument" De quel getattr vous parlez, le getattr (self.tmp, attr) ou le getattr (a, 'undefined_attribute', 3) en dehors de la classe.


@ TuấnĐoàn appellent tous les deux à l'attribut not_important_attribute de la classe ClassOne , regardez ma réponse pour plus de détails


Je parle de getattr (a, 'undefined_attribute', 3). getattr (self.tmp, attr) lève AttributeError et getattr (a, 'undefined_attribute', 3) renvoie 3 à cause de la valeur par défaut


@AndreyBerenda lorsque la méthode __getattr__ est appelée, elle retournera la valeur que nous l'avons définie à l'intérieur de la fonction, pas la valeur par défaut en dehors de la classe, vérifiez ceci


Je parle de l'impression de cas (getattr (b, 'undefined_attribute', 3))



1
votes

Pas à pas pourquoi la valeur 3 est affichée:

la super classe a un attribut not_important_attribute et il est défini lorsque le constructeur est appelé

b = ClassOne("not_important_string")
print(getattr(b,'not_important_attribute',3))
not_important_string # founds the method
print(getattr(b,'any',3))
3 # doesn't found method, returns default  

Ici, dans le constructeur ClassTwo , vous créez une instance de ClassOne et l'enregistrez dans la variable tmp . Cela signifie que lorsque vous remplacez __getattr__ , vous demanderez la valeur de l'attribut de ClassOne

print(getattr(a,'not_important_attribute',3))
not_important_string # founds the method
print(getattr(a,'any',3))
3 #doesn't found method, returns default

C'est la même chose de faire directement:

class ClassOne:
    def __init__(self,some_object):
        self.not_important_attribute=some_object   


0 commentaires

0
votes

Lorsque vous appelez getattr (a, 'undefined_attribute', 3) , vous appelez la fonction standard python getattr et lui passez une valeur par défaut. Cela enveloppe en fait votre getattr personnalisé dans ClassTwo. Nous pouvons le voir en modifiant un petit peu getattr .

   def __getattr__(self, attr, *args, **kwargs):
        print(args)  # -> empty
        print(kwargs)  # -> empty
        return getattr(self.tmp, attr)

Vous pouvez contourner ce wrapping et appeler getattr directement depuis ClassTwo, en utilisant print (a .__ getattr __ ('undefined_attribute', 3)) . Cette méthode soulèverait une exception.

Donc, essentiellement getattr (a, 'undefined_attribute', 3) est la méthode python standard qui appelle en interne le getattr personnalisé de ClassTwo.


0 commentaires

0
votes
class ClassTwo:
...
    def __getattr__(self,attr):
        return getattr(self.tmp,attr)
getattr(a,'undefined_attribute',3) method is a wrapper to call __getattribute__ and if __getattribute__ fails, it will call __getattr__. It is implemented with handling of exception AttributeError. In your case, getattr(a,'undefined_attribute',3) calls ClassTwo.__getattr__ above. It seems you think  when it reaches return getattr(self.tmp,attr), it will throw errors or return some kind of errors and break and stop at that point complete. However, in python, program will pass exception up the call stack. Along the upstream of the call stack, if that exception gets handle, it will exit/complete normally. In this case, AttributeError get passed back along the call stack to getattr(a,'undefined_attribute',3). this getattr has default value for AttributeError so it returns 3 and exits normally.

0 commentaires