Создать декоратор, который может видеть текущий метод класса - PullRequest
4 голосов
/ 14 сентября 2010

Можете ли вы создать декоратор внутри класса, который будет видеть методы и переменные классов?

Декоратор здесь не видит: 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)

Ответы [ 3 ]

5 голосов
/ 14 сентября 2010

Нет необходимости реализовывать этот декоратор как класс, и нет необходимости реализовывать его внутри определения класса Foo. Достаточно будет следующего:

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

Использование этого декоратора, похоже, работает:

>>> 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
1 голос
/ 14 сентября 2010

Вы можете иметь класс, но вам нужно использовать протокол дескриптора

 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)

Вам всегда нужно использовать дескриптор, когда вы хотите украсить методы класса экземпляром класса, используя метод __call__. Причина этого заключается в том, что будет передан только один self, который ссылается на экземпляр класса декорирования, а не на экземпляр декорированного метода.

1 голос
/ 14 сентября 2010

Мой предыдущий ответ был сделан на скорую руку. Если вы хотите написать декоратор, вы действительно должны использовать wraps из модуля functools. Он заботится о тяжелых вещах для вас.

Правильный способ определения декоратора canRun:

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

Функция canRun должна быть определена вне класса.

...