Create decorator that can see current class method

早过忘川 提交于 2019-12-07 11:36:29

问题


Can you create a decorator inside a class that will see the classes methods and variables?

The decorator here doesnt see: self.longcondition()

class Foo:
    def __init__(self, name):
        self.name = name

    # decorator that will see the self.longcondition ???
    class canRun(object):
            def __init__(self, f):
                self.f = f

            def __call__(self, *args):
                if self.longcondition(): # <-------- ???
                    self.f(*args)

    # this is supposed to be a very long condition :)
    def longcondition(self):
        return isinstance(self.name, str)

    @canRun # <------
    def run(self, times):
        for i in xrange(times):
            print "%s. run... %s" % (i, self.name)

回答1:


You can have it be a class but you need to use the descriptor protocol

 import types

 class canRun(object):
    def __init__(self, f):
        self.f = f
        self.o = object  # <-- What the hell is this about? 

    def __call__(self, *args):
        if self.longcondition():
            self.f(*args)

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

You always need to use a descriptor when you want to decorate class methods with a class instance using the __call__ method. The reason for this is that there will only be one self passed in which refers to the instance of the decorating class and not the instance of the decorated method.




回答2:


There's no real need to implement this decorator as a class, and there's no need to implement it inside the definition of the Foo class. The following will suffice:

def canRun(meth):
    def decorated_meth(self, *args, **kwargs):
        if self.longcondition():
            print 'Can run'
            return meth(self, *args, **kwargs)
        else:
            print 'Cannot run'
            return None
    return decorated_meth

Using that decorator seems to work:

>>> Foo('hello').run(5)
Can run
0. run... hello
1. run... hello
2. run... hello
3. run... hello
4. run... hello
>>> Foo(123).run(5)
Cannot run



回答3:


My previous answer was made in haste. If you're wanting to write a decorator you should really use wraps from the functools module. It takes care of the hard stuff for you.

A proper way to define the canRun decorator is:

from functools import wraps
def canRun(f):
  @wraps(f)
  def wrapper(instance, *args):
    if instance.longcondition():
      return f(instance, *args)
  return wrapper

The canRun function should be defined outside of the class.



来源:https://stackoverflow.com/questions/3704392/create-decorator-that-can-see-current-class-method

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!