2009-08-11 4 views
89

Можно написать STH как:Python декораторы в классах

class Test(object): 
    def _decorator(self, foo): 
     foo() 

    @self._decorator 
    def bar(self): 
     pass 

Это не удается: само в @self неизвестно

Я также попытался:

@Test._decorator(self) 

который также терпит неудачу: Тест неизвестно

Если хотите температуру. измените некоторые переменные экземпляра в декораторе и запустите оформленный метод, перед тем, как смените их.

Спасибо.

ответ

52

То, что вы хотите сделать, невозможно. Возьмем, к примеру, или не ниже выглядит действительным код:

class Test(object): 

    def _decorator(self, foo): 
     foo() 

    def bar(self): 
     pass 
    bar = self._decorator(bar) 

Это, конечно, не действует, так как self не определена в этой точке. То же самое относится к Test, поскольку он не будет определен до тех пор, пока не будет определен сам класс (который находится в процессе). Я показываю вам этот фрагмент кода, потому что это то, что преобразует ваш фрагмент декоратора.

Таким образом, как вы можете видеть, доступ к экземпляру в таком декораторе невозможен, поскольку декораторы применяются во время определения любой функции/метода, к которой они привязаны, а не во время создания экземпляра.

Если вам нужен доступ на уровне класса, попробуйте следующее:

class Test(object): 

    @classmethod 
    def _decorator(cls, foo): 
     foo() 

    def bar(self): 
     pass 
Test.bar = Test._decorator(Test.bar) 
+6

"Impossible" фактически неверно. – zneak

+3

, вероятно, необходимо обновить, чтобы получить более точный ответ ниже – natb1

168

Будет что-то вроде этого делать то, что вам нужно?

class Test(object): 
    def _decorator(foo): 
     def magic(self) : 
      print "start magic" 
      foo(self) 
      print "end magic" 
     return magic 

    @_decorator 
    def bar(self) : 
     print "normal call" 

test = Test() 

test.bar() 

Это позволяет избежать вызова к себе, чтобы получить доступ к декоратору и оставляет его скрытым в пространстве имен класса как обычный метод.

>>> import stackoverflow 
>>> test = stackoverflow.Test() 
>>> test.bar() 
start magic 
normal call 
end magic 
>>> 

отредактирован ответить на вопрос в комментариях:

Как использовать скрытый декоратора в другом классе

class Test(object): 
    def _decorator(foo): 
     def magic(self) : 
      print "start magic" 
      foo(self) 
      print "end magic" 
     return magic 

    @_decorator 
    def bar(self) : 
     print "normal call" 

    _decorator = staticmethod(_decorator) 

class TestB(Test): 
    @Test._decorator 
    def bar(self): 
     print "override bar in" 
     super(TestB, self).bar() 
     print "override bar out" 

print "Normal:" 
test = Test() 
test.bar() 
print 

print "Inherited:" 
b = TestB() 
b.bar() 
print 
+0

Спасибо за ваш ответ. Да, это сработало бы, если бы не тот факт, что я хотел, чтобы декоратор выполнил некоторые операции с переменными экземпляра - и это потребует «я». – hcvst

+4

Декоратор или украшенная функция? Обратите внимание, что возвращаемая функция «магия», которая обертывает панель, получает переменную self выше, когда «вызов» вызывается в экземпляре и может делать что-либо с переменными экземпляра, которые ему нужны до и после (или даже если они называются «баром») , При объявлении класса нет такой вещи, как переменные экземпляра. Вы хотели что-то сделать в классе из декоратора? Я не думаю, что это идиоматическое использование. –

+0

Спасибо, Майкл, только теперь увидел, что это то, что я хотел. – hcvst

2

Я нашел этот вопрос, исследуя очень похожие проблемы. Мое решение состоит в том, чтобы разделить проблему на две части. Во-первых, вам нужно захватить данные, которые вы хотите связать с методами класса. В этом случае handler_for свяжет команду Unix с обработчиком для вывода этой команды.

class OutputAnalysis(object): 
    "analyze the output of diagnostic commands" 
    def handler_for(name): 
     "decorator to associate a function with a command" 
     def wrapper(func): 
      func.handler_for = name 
      return func 
     return wrapper 
    # associate mount_p with 'mount_-p.txt' 
    @handler_for('mount -p') 
    def mount_p(self, slurped): 
     pass 

Теперь, когда мы связали некоторые данные с каждым методом класса, нам необходимо собрать эти данные и сохранить их в атрибуте класса.

OutputAnalysis.cmd_handler = {} 
for value in OutputAnalysis.__dict__.itervalues(): 
    try: 
     OutputAnalysis.cmd_handler[value.handler_for] = value 
    except AttributeError: 
     pass 
4

Я использую этот тип декоратора в некоторых ситуациях отладки, что позволяет верховные свойства класса от декорирования, без того, чтобы найти функцию вызова.

class myclass(object): 
    def __init__(self): 
     self.property = "HELLO" 

    @adecorator(property="GOODBYE") 
    def method(self): 
     print self.property 

Вот код декоратор

class adecorator (object): 
    def __init__ (self, *args, **kwargs): 
     # store arguments passed to the decorator 
     self.args = args 
     self.kwargs = kwargs 

    def __call__(self, func): 
     def newf(*args, **kwargs): 

      #the 'self' for a method function is passed as args[0] 
      slf = args[0] 

      # replace and store the attributes 
      saved = {} 
      for k,v in self.kwargs.items(): 
       if hasattr(slf, k): 
        saved[k] = getattr(slf,k) 
        setattr(slf, k, v) 

      # call the method 
      ret = func(*args, **kwargs) 

      #put things back 
      for k,v in saved.items(): 
       setattr(slf, k, v) 

      return ret 
     newf.__doc__ = func.__doc__ 
     return newf 

Примечание: потому что я использовал класс декоратора вам нужно использовать @adecorator() со скобками на декорировать функции, даже если вы не передадите аргументы конструктору класса декоратора.

1

Декораторов кажется лучше подходит для изменения функциональных возможностей всего объекта (в том числе функциональных объектов) в зависимости от функциональности методы объекта, который в общем случае будет зависеть от атрибутов экземпляра. Например:

def mod_bar(cls): 
    # returns modified class 

    def decorate(fcn): 
     # returns decorated function 

     def new_fcn(self): 
      print self.start_str 
      print fcn(self) 
      print self.end_str 

     return new_fcn 

    cls.bar = decorate(cls.bar) 
    return cls 

@mod_bar 
class Test(object): 
    def __init__(self): 
     self.start_str = "starting dec" 
     self.end_str = "ending dec" 

    def bar(self): 
     return "bar" 

Выход:

>>> import Test 
>>> a = Test() 
>>> a.bar() 
starting dec 
bar 
ending dec 
0

Вы можете украсить декоратора:

import decorator 

class Test(object): 
    @decorator.decorator 
    def _decorator(foo, self): 
     foo(self) 

    @_decorator 
    def bar(self): 
     pass 
2

Это один из способов я не знаю (и используется) для доступа self из внутри декоратора, определенного внутри того же класса:

class Thing(object): 
    def __init__(self, name): 
     self.name = name 

    def debug_name(function): 
     def debug_wrapper(*args): 
      self = args[0] 
      print 'self.name = ' + self.name 
      print 'running function {}()'.format(function.__name__) 
      function(*args) 
      print 'self.name = ' + self.name 
     return debug_wrapper 

    @debug_name 
    def set_name(self, new_name): 
     self.name = new_name 

Выход (проверено на Python 2.7.10):

>>> a = Thing('A') 
>>> a.name 
'A' 
>>> a.set_name('B') 
self.name = A 
running function set_name() 
self.name = B 
>>> a.name 
'B' 

В приведенном выше примере это глупо, но показывает, что он работает.

3
class Example(object): 

    def wrapper(func): 
     @functools.wraps(func) 
     def wrap(self, *args, **kwargs): 
      print "inside wrap" 
      return func(self, *args, **kwargs) 
     return wrap 

    @wrapper 
    def method(self): 
     pass 

    wrapper = staticmethod(wrapper) 
1

Вот расширение на ответ Майкла Спира взять его на несколько шагов дальше:

Экземпляр метод декоратора, который принимает аргументы и действует на функции с аргументами и возвращаемым значением.

class Test(object): 
    "Prints if x == y. Throws an error otherwise." 
    def __init__(self, x): 
     self.x = x 

    def _outer_decorator(y): 
     def _decorator(foo): 
      def magic(self, *args, **kwargs) : 
       print("start magic") 
       if self.x == y: 
        return foo(self, *args, **kwargs) 
       else: 
        raise ValueError("x ({}) != y ({})".format(self.x, y)) 
       print("end magic") 
      return magic 

     return _decorator 

    @_outer_decorator(y=3) 
    def bar(self, *args, **kwargs) : 
     print("normal call") 
     print("args: {}".format(args)) 
     print("kwargs: {}".format(kwargs)) 

     return 27 

А потом

In [2]: 

    test = Test(3) 
    test.bar(
     13, 
     'Test', 
     q=9, 
     lollipop=[1,2,3] 
    ) 
    ​ 
    start magic 
    normal call 
    args: (13, 'Test') 
    kwargs: {'q': 9, 'lollipop': [1, 2, 3]} 
Out[2]: 
    27 
In [3]: 

    test = Test(4) 
    test.bar(
     13, 
     'Test', 
     q=9, 
     lollipop=[1,2,3] 
    ) 
    ​ 
    start magic 
    --------------------------------------------------------------------------- 
    ValueError        Traceback (most recent call last) 
    <ipython-input-3-576146b3d37e> in <module>() 
      4  'Test', 
      5  q=9, 
    ----> 6  lollipop=[1,2,3] 
      7) 

    <ipython-input-1-428f22ac6c9b> in magic(self, *args, **kwargs) 
     11      return foo(self, *args, **kwargs) 
     12     else: 
    ---> 13      raise ValueError("x ({}) != y ({})".format(self.x, y)) 
     14     print("end magic") 
     15    return magic 

    ValueError: x (4) != y (3)