python - with - Décorateurs et méthode de classe




python useful decorators (2)

Dans une définition de classe, __call__ est une fonction, pas une méthode. Le fait d'accéder à la fonction via la recherche d'attribut (par exemple en utilisant la syntaxe point), comme avec Adder2.__call__ , renvoie une méthode non liée. Et a2.__call__ renvoie une méthode liée (avec self lié à a2 ).

Notez que dans Python3, le concept de unbound method a été abandonné. Là, Adder2.__call__ est aussi une fonction.

J'ai du mal à comprendre pourquoi ce qui suit arrive. J'ai un décorateur qui ne fait rien sauf qu'il vérifie si une fonction est une méthode. Je pensais avoir compris la méthode en Python, mais évidemment, ce n'est pas le cas:

import inspect

def deco(f):
    def g(*args):
        print inspect.ismethod(f)
        return f(*args)
    return g

class Adder:
    @deco
    def __call__(self, a):
        return a + 1

class Adder2:
    def __call__(self, a):
        return a + 2
Adder2.__call__ = deco(Adder2.__call__)

Maintenant, en exécutant ce qui suit:

>>> a = Adder()
>>> a(1)
False
2
>>> a2 = Adder2()
>>> a2(1)    
True
3

Je m'attendrais à ce que ce code imprime True deux fois.

Ainsi, la décoration manuelle de la fonction comme dans l'Adder2 n'est pas l'équivalent exact de la décoration via la fonction @deco?

Quelqu'un peut-il être si heureux et expliquer pourquoi cela arrive?


Les méthodes sont des fonctions associées à une classe. Les méthodes sont créées uniquement lorsque vous les récupérez à partir d'une classe déjà définie. une méthode est un wrapper autour d'une fonction, avec une référence à la classe (et éventuellement une référence à l'instance).

Ce qui se passe dans le premier cas est: Python compile votre Adder définition de classe. Il trouve la définition du décorateur et une fonction. Le décorateur est passé la fonction, renvoyant une nouvelle fonction. Cette fonction est ajoutée à la définition de la classe (stockée dans la classe __dict__ ). Pendant tout ce temps, vous avez affaire à une fonction python, pas à une méthode. Cela arrive plus tard.

Lorsque vous appelez ensuite a(1) , une recherche révèle que l'instance n'a pas de __call__ mais la classe Adder fait, donc elle est récupérée en utilisant __getattribute__() . Cela trouve une fonction (votre décorateur deco ), qui est un descripteur de sorte que __get__() méthode est appelée (donc Adder.__call__.__get__(a, Adder)) , renvoyant une méthode liée , qui est ensuite appelée et transmise dans 1 valeur. La méthode est liée car instance n'est pas None lorsque __get__() est appelé. Votre décorateur, qui a enveloppé une fonction au moment de la construction de la classe, imprime False parce qu'il a été passé une fonction déballée pour commencer.

Dans le second cas, cependant, vous récupérez une méthode (encore une fois via __getattribute__() appelant __get__() sur la fonction non Adder2.__call__ ), cette fois indépendante (car il n'y a pas d'instance, seule une classe est passée à __get__() (la L'appel complet est Adder2.__call__.__get__(None, Adder2) ), et vous décorez ensuite cette méthode. Maintenant ismethod() imprime True.

Notez que dans Python 3, le dernier cas change. Dans Python 3, il n'y a plus de concept de méthode non liée, seulement des fonctions et des méthodes liées. Le terme «lié» est donc complètement abandonné. Votre deuxième cas Adder2.__call__.__get__(None, Adder2) également False sous Adder2.__call__.__get__(None, Adder2) renvoie une fonction dans ce cas.







python-decorators