Декораторы Python в занятия
можно ли написать sth как:
class Test(object):
def _decorator(self, foo):
foo()
@self._decorator
def bar(self):
pass
это не удается: self в @self неизвестно
Я тоже пробовал:
@Test._decorator(self)
что также не удается: тест неизвестен
Если бы темп. изменение некоторых переменных экземпляра
в декораторе и запустить украшенный метод, прежде чем
меняю их обратно.
спасибо.
9 ответов:
то, что вы хотите сделать, невозможно. Возьмем, например, выглядит ли приведенный ниже код допустимым:
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)
будет ли что-то вроде этого делать то, что вам нужно?
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()это позволяет избежать вызова self для доступа к декоратору и оставляет его скрытым в пространстве имен класса как обычный метод.
>>> 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
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)
Я использую этот тип декоратора в некоторых ситуациях отладки, он позволяет переопределять свойства класса путем украшения, без необходимости поиска вызывающей функции.
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 ()скобки on для украшения функций, даже если вы не передаете никаких аргументов конструктору класса декоратора.
Это один из способов, который я знаю (и использовал) для доступа
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'пример выше глупо, но показывает, что это работает.
Я нашел этот вопрос, исследуя очень похожие проблемы. Мое решение-разделить проблему на две части. Во-первых, необходимо захватить данные, которые вы хотите связать с методами класса. В этом случае 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
вот расширение ответа Майкла Шпеера, чтобы сделать еще несколько шагов:
метод экземпляра декоратор, который принимает аргументы и действует на функцию с аргументами и возвращаемым значением.
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)
декораторы, кажется, лучше подходят для изменения функциональности весь объект (включая объекты функций) по сравнению с функциональностью объект метод, который в целом будет зависеть от атрибутов экземпляра. Например:
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
вы можете украсить оформитель:
import decorator class Test(object): @decorator.decorator def _decorator(foo, self): foo(self) @_decorator def bar(self): pass
Comments