3 votes

Comment gérer et retourner les propriétés ET les fonctions manquantes dans une classe Python en utilisant la fonction __getattr__ ?

Il est assez facile d'utiliser le __getattr__ méthode spéciale sur les classes Python pour gérer les propriétés ou les fonctions manquantes, mais apparemment pas les deux en même temps.

Considérons cet exemple qui traite toute propriété demandée qui n'est pas définie explicitement ailleurs dans la classe...

class Props:
    def __getattr__(self, attr):
        return 'some_new_value'

>>> p = Props()
>>> p.prop                          # Property get handled
'some_new_value'

>>> p.func('an_arg', kw='keyword')  # Function call NOT handled
Traceback (most recent call last):
  File "<console>", line 1, in <module>
TypeError: 'str' object is not callable

Ensuite, considérons cet exemple qui gère tout appel de fonction non défini explicitement ailleurs dans la classe...

class Funcs:
    def __getattr__(self, attr):
        def fn(*args, **kwargs):
            # Do something with the function name and any passed arguments or keywords
            print attr
            print args
            print kwargs
            return 
        return fn

>>> f = Funcs()
>>> f.prop                          # Property get NOT handled
<function fn at 0x10df23b90>

>>> f.func('an_arg', kw='keyword')  # Function call handled
func
('an_arg',)
{'kw': 'keyword'}

La question est de savoir comment traiter les deux types d'attributs manquants dans le même fichier. __getattr__ ? Comment détecter si l'attribut demandé est en notation propriété ou en notation méthode avec des parenthèses et retourner respectivement une valeur ou une fonction ? Essentiellement, je veux gérer QUELQUES attributs de propriété manquants ET QUELQUES attributs de fonction manquants, puis recourir au comportement par défaut pour tous les autres cas.

Des conseils ?

3voto

abarnert Points 94246

Comment détecter si l'attribut demandé était en notation de propriété ou en notation de méthode avec des parenthèses et renvoyer respectivement une valeur ou une fonction ?

Tu ne peux pas. Vous ne pouvez pas non plus dire si une méthode demandée est une instance, une classe, une méthode statique, etc. Tout ce que vous pouvez dire, c'est que quelqu'un essaie de récupérer un attribut pour un accès en lecture. Rien d'autre n'est passé dans la machine getattribute, donc rien d'autre n'est disponible pour votre code.

Vous avez donc besoin d'une méthode hors bande pour savoir s'il faut créer une fonction ou un autre type de valeur. C'est en fait assez commun - vous pouvez en fait être le mandataire d'un autre objet qui fait ont une distinction valeur/fonction (pensez à ctypes ou PyObjC), ou vous avez peut-être une convention de dénomination, etc.

Cependant, vous pouvez toujours renvoyer un objet qui peut être utilisé dans les deux cas. Par exemple, si votre "comportement par défaut" consiste à renvoyer des attributs qui sont des entiers, ou des fonctions qui renvoient un entier, vous pouvez renvoyer quelque chose comme ceci :

class Integerizer(object):
    def __init__(self, value):
        self.value = value
    def __int__(self):
        return self.value
    def __call__(self, *args, **kw):
        return self.value

1voto

Martijn Pieters Points 271458

Il n'y a aucun moyen de détecter comment l'attribut retourné était destiné à être utilisé. Tout sur les objets python sont des attributs, y compris les méthodes :

>>> class Foo(object):
...     def bar(self): print 'bar called'
...     spam='eggs'
... 
>>> Foo.bar
<unbound method Foo.bar>
>>> Foo.spam
'eggs'

Python recherche d'abord l'attribut ( bar ou spam ), et si vous voulez appelez (parenthèses ajoutées), alors Python invoque l'appelable après rechercher l'attribut :

>>> foo = Foo()
>>> fbar = foo.bar
>>> fbar()
'bar called'

Dans le code ci-dessus, j'ai séparé la recherche de bar d'appeler bar .

Puisqu'il n'y a pas de distinction, vous ne pouvez pas détecter en __getattr__ à quoi servira l'attribut retourné.

__getattr__ est appelé chaque fois que l'accès normal aux attributs échoue ; dans l'exemple suivant monty est défini sur la classe, donc __getattr__ est no appelé ; il n'est appelé que pour bar.eric y bar.john :

>>> class Bar(object):
...     monty = 'python'
...     def __getattr__(self, name):
...         print 'Attribute access for {0}'.format(name)
...         if name == 'eric':
...             return 'idle'
...         raise AttributeError(name)
... 
>>> bar = Bar()
>>> bar.monty
'python'
>>> bar.eric
Attribute access for eric
'idle'
>>> bar.john
Attribute access for john
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in __getattr__
AttributeError: john

Notez que les fonctions ne sont pas les seuls objets que vous pouvez invoquer (appeler) ; toute classe personnalisée qui implémente la fonction __call__ fera l'affaire :

>>> class Baz(object):
...    def __call__(self, name):
...        print 'Baz sez: "Hello {0}!"'.format(name)
...
>>> baz = Baz()
>>> baz('John Cleese')
Baz sez: "Hello John Cleese!"

Vous pourriez utiliser ce retour d'objets de __getattr__ qui peut à la fois être appelé et utilisé comme valeur dans différents contextes.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X