python - Comment décorer une méthode d'instance avec une classe de décorateur?




self python-decorators (2)

J'utilise les décorateurs de la manière suivante:

def timeit(method):
    def timed(*args, **kw):
        ts = time.time()
        result = method(*args, **kw)
        te = time.time()
        ts = round(ts * 1000)
        te = round(te * 1000)
        print('%r (%r, %r) %2.2f millisec' %
             (method.__name__, args, kw, te - ts))
        return result
    return timed


 class whatever(object):
    @timeit
    def myfunction(self):
         do something

Considérez ce petit exemple:

import datetime as dt

class Timed(object):
    def __init__(self, f):
        self.func = f

    def __call__(self, *args, **kwargs):
        start = dt.datetime.now()
        ret = self.func(*args, **kwargs)
        time = dt.datetime.now() - start
        ret["time"] = time
        return ret

class Test(object):
    def __init__(self):
        super(Test, self).__init__()

    @Timed
    def decorated(self, *args, **kwargs):
        print(self)
        print(args)
        print(kwargs)
        return dict()

    def call_deco(self):
        self.decorated("Hello", world="World")

if __name__ == "__main__":
    t = Test()
    ret = t.call_deco()

qui imprime

Hello
()
{'world': 'World'}

Pourquoi le paramètre self (qui devrait être l'instance de test obj) n'est-il pas passé en tant que premier argument de la fonction decorated ?

Si je le fais manuellement, comme:

def call_deco(self):
    self.decorated(self, "Hello", world="World")

cela fonctionne comme prévu. Mais si je dois savoir à l'avance si une fonction est décorée ou non, cela irait à l'encontre de l'objectif des décorateurs. Quelle est la tendance à aller ici, ou ai-je mal compris quelque chose?


Vous devez d’abord comprendre comment la fonction devient une méthode et comment self -même s’injecte «automatiquement» .

Une fois que vous savez cela, le "problème" est évident: vous decorated fonction decorated avec une instance Timed - IOW, Test.decorated est une instance Timed , pas une instance de function - et votre classe Timed ne reproduit pas l'implémentation du type de fonction de le protocole de descriptor . Ce que vous voulez ressemble à ceci:

import types

class Timed(object):
    def __init__(self, f):
        self.func = f

    def __call__(self, *args, **kwargs):
        start = dt.datetime.now()
        ret = self.func(*args, **kwargs)
        time = dt.datetime.now() - start
        ret["time"] = time
        return ret

   def __get__(self, instance, cls):           
       return types.MethodType(self, instance, cls)




python-decorators