Python decorators in classes

You can write something like:

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

This fails: self in @self unknown

I also tried:

 @Test._decorator(self) 

which also fails: unknown test

I would like to temporarily change some instance variables in the decorator, and then run the decorated method before changing them.

+121
python class decorator self
Aug 11 '09 at 23:01
source share
9 answers

Will something like this do what you need?

 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() 

This eliminates the call to self to access the decorator and leaves it hidden in the class namespace as a normal method.

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



edited to answer the question in the comments:

How to use hidden decorator in another class

 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 

Exit:

 Normal: start magic normal call end magic Inherited: start magic override bar in start magic normal call end magic override bar out end magic 
+235
Aug 12 '09 at 1:13
source share

What you want to do is impossible. Take for example whether the code below is valid:

 class Test(object): def _decorator(self, foo): foo() def bar(self): pass bar = self._decorator(bar) 

This, of course, is not valid, since self not defined at this point. The same applies to Test , because it will not be defined until the class itself (which is in the process) is defined. I will show you this piece of code, because this is what transforms the fragment of your decorator.

So, as you can see, access to an instance in such a decorator is not possible, since decorators are used during the definition of any function / method to which they are bound, and not during instance creation.

If you need class-level access , try the following:

 class Test(object): @classmethod def _decorator(cls, foo): foo() def bar(self): pass Test.bar = Test._decorator(Test.bar) 
+54
Aug 11 '09 at 23:33
source share
 import functools class Example: 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): print("METHOD") wrapper = staticmethod(wrapper) e = Example() e.method() 
+18
Nov 17 '16 at 12:48
source share

This is one of the ways I know (and used) access to self from inside a decorator defined inside the same class:

 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 

Exit (tested on 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' 

The above example is stupid, but shows that it works.

+7
Jun 07 '16 at 9:43
source share

I use this type of decorator in some debugging situations; it allows you to override class properties by decorating without having to find the calling function.

 class myclass(object): def __init__(self): self.property = "HELLO" @adecorator(property="GOODBYE") def method(self): print self.property 

Here is the decorator code

 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 

Note: since I used the class decorator, you will need to use @adecorator () with parentheses to decorate the functions, even if you do not pass any arguments to the constructor of the decorator class.

+6
Aug 20 '12 at 9:13
source share

I found this question exploring a very similar problem. My solution is to split the problem into two parts. First, you need to capture the data that you want to associate with the class methods. In this case, handler_for will associate the Unix command with a handler to output this command.

 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 

Now that we have associated some data with each method of the class, we need to collect this data and store it in the class attribute.

 OutputAnalysis.cmd_handler = {} for value in OutputAnalysis.__dict__.itervalues(): try: OutputAnalysis.cmd_handler[value.handler_for] = value except AttributeError: pass 
+4
Jan 17 2018-12-17T00:
source share

Decorators seem more suitable for changing the functionality of the whole object (including function objects) compared to the functionality of the object method , which as a whole will depend on the attributes of the instance. For example:

 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" 

Output:

 >>> import Test >>> a = Test() >>> a.bar() starting dec bar ending dec 
+3
Aug 27 '13 at 2:22
source share

Here the Michael Speer extension will answer a few steps further:

The decoder of an instance method that takes arguments and acts on a function with arguments and return value.

 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 

And then

 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) 
+3
Aug 09 '17 at 23:57 on
source share

You can decorate the decorator:

 import decorator class Test(object): @decorator.decorator def _decorator(foo, self): foo(self) @_decorator def bar(self): pass 
+1
Aug 13 '14 at 15:37
source share



All Articles