I want to make sure that I understood correctly how decorator as class works.
Let's say i have a decorator as a function that add an attribute to an object
def addx(obj):
obj.x = 10
return obj
#addx
class A:
pass
assert A.x == 10
Is it possible to write the same decorator as a class decorator? since the class decorator can't return the object itself with __init__
class addx:
def __init__(self, obj):
obj.x = 10
# ???
You could write an equivalent class-based decorator like this...
class addx:
def __new__(self, obj):
obj.x = 10
return obj
#addx
class A:
pass
assert A.x == 10
...but I don't think this really gets you anything. The utility of a class-based decorator becomes more apparent when your goal is to modify objects of class A, rather than class A itself. Compare the following two decorators, one function based and one class based:
def addx_func(kls):
def wrapper():
res = kls()
res.x = 10
return res
return wrapper
class addx_class:
def __init__(self, kls):
self.kls = kls
def __call__(self):
res = self.kls()
res.x = 10
return res
#addx_func
class A:
pass
#addx_class
class B:
pass
a = A()
assert a.x == 10
b = B()
assert b.x == 10
Can one write something like:
class Test(object):
def _decorator(self, foo):
foo()
#self._decorator
def bar(self):
pass
This fails: self in #self is unknown
I also tried:
#Test._decorator(self)
which also fails: Test unknown
I would like to temporarily change some instance variables
in the decorator and then run the decorated method, before
changing them back.
Would 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 avoids the call to self to access the decorator and leaves it hidden in the class namespace as a regular method.
>>> import stackoverflow
>>> test = stackoverflow.Test()
>>> test.bar()
start magic
normal call
end magic
>>>
edited to answer question in comments:
How to use the 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
Output:
Normal:
start magic
normal call
end magic
Inherited:
start magic
override bar in
start magic
normal call
end magic
override bar out
end magic
What you're wanting to do isn't possible. Take, for instance, whether or not the code below looks valid:
class Test(object):
def _decorator(self, foo):
foo()
def bar(self):
pass
bar = self._decorator(bar)
It, of course, isn't valid since self isn't defined at that point. The same goes for Test as it won't be defined until the class itself is defined (which its in the process of). I'm showing you this code snippet because this is what your decorator snippet transforms into.
So, as you can see, accessing the instance in a decorator like that isn't really possible since decorators are applied during the definition of whatever function/method they are attached to and not during instantiation.
If you need class-level access, try this:
class Test(object):
#classmethod
def _decorator(cls, foo):
foo()
def bar(self):
pass
Test.bar = Test._decorator(Test.bar)
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()
This is one way to access(and have used) 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
Output (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 example above is silly, but it works.
Here's an expansion on Michael Speer's answer to take it a few steps further:
An instance method decorator which takes arguments and acts on a function with arguments and a 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)
I found this question while researching 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 a Unix command with handler for that command's output.
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've associated some data with each class method, we need to gather that data and store it in a class attribute.
OutputAnalysis.cmd_handler = {}
for value in OutputAnalysis.__dict__.itervalues():
try:
OutputAnalysis.cmd_handler[value.handler_for] = value
except AttributeError:
pass
I use this type of decorator in some debugging situations, it allows overriding 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: because I've used a class decorator you'll need to use #adecorator() with the brackets on to decorate functions, even if you don't pass any arguments to the decorator class constructor.
The simple way to do it.
All you need is to put the decorator method outside the class.
You can still use it inside.
def my_decorator(func):
#this is the key line. There's the aditional self parameter
def wrap(self, *args, **kwargs):
# you can use self here as if you were inside the class
return func(self, *args, **kwargs)
return wrap
class Test(object):
#my_decorator
def bar(self):
pass
Declare in inner class.
This solution is pretty solid and recommended.
class Test(object):
class Decorators(object):
#staticmethod
def decorator(foo):
def magic(self, *args, **kwargs) :
print("start magic")
foo(self, *args, **kwargs)
print("end magic")
return magic
#Decorators.decorator
def bar( self ) :
print("normal call")
test = Test()
test.bar()
The result:
>>> test = Test()
>>> test.bar()
start magic
normal call
end magic
>>>
Decorators seem better suited to modify the functionality of an entire object (including function objects) versus the functionality of an object method which in general will depend on instance attributes. 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"
The output is:
>>> import Test
>>> a = Test()
>>> a.bar()
starting dec
bar
ending dec
I have a Implementation of Decorators that Might Help
import functools
import datetime
class Decorator(object):
def __init__(self):
pass
def execution_time(func):
#functools.wraps(func)
def wrap(self, *args, **kwargs):
""" Wrapper Function """
start = datetime.datetime.now()
Tem = func(self, *args, **kwargs)
end = datetime.datetime.now()
print("Exection Time:{}".format(end-start))
return Tem
return wrap
class Test(Decorator):
def __init__(self):
self._MethodName = Test.funca.__name__
#Decorator.execution_time
def funca(self):
print("Running Function : {}".format(self._MethodName))
return True
if __name__ == "__main__":
obj = Test()
data = obj.funca()
print(data)
You can decorate the decorator:
import decorator
class Test(object):
#decorator.decorator
def _decorator(foo, self):
foo(self)
#_decorator
def bar(self):
pass
Given the following example:
class A:
def f(self, x):
return 2*x
I would like to write another method which uses f above but adds a constant, i.e.
class A:
def f(self, x):
return 2*x
def g(self, x):
return self.f(x) + 10
This would be one way. However, this smells very much like decorating! What would be the proper pythonic way to do this?
Extending answer of #Ajax1234, you may try to:
- parametrize your decorator,
- call your decorator explicitly (without #).
def add_val(_value):
def decorator(f):
def wrapper(cls, _x):
return f(cls, _x) + _value
return wrapper
return decorator
class A:
def f(self, x):
return 2*x
g = add_val(10)(f)
[EDIT]
You may also improve the decorator with functools.wraps() (which is a decorator itself). All you need to do is to change your wrapper declaration:
#functools.wraps(f)
def wrapper(cls, _x):
return f(cls, _x) + _value
You can write a simple function outside the class:
def add_val(f):
def wrapper(cls, _x):
return f(cls, _x) + 10
return wrapper
class A:
#add_val
def f(self, x):
return 2*x
print(A().f(20))
Output:
50
Edit: you can utilize functools.wraps with a classmethod in Python3. The wrapped function f will return x*2 and the decorator g will add 10 to the returned result of the function passed to it. However, to be able to save the original functionality of f, you can utilize the __wrapped__ attribute:
import functools
def g(f):
#functools.wraps(f)
def wrapper(cls, _x):
return f(cls, _x)+10
return wrapper
class A:
#classmethod
#g
def f(cls, x):
return x*2
f_1 = functools.partial(A.f.__wrapped__, A)
print(A.f(4))
print(f_1(4))
Output:
18
8
I want to write class decorator which for all non-magic methods, to decorate these methods. The idea is that to all methods of class print its name after call; I do not want decor all methods but only class. Decorator log_method works. I have problem with log_class decorator. There are no errors and no output.
import traceback
import inspect
def log_method(func):
def inner(*args, **kwargs):
print("{}{}".format(int(len(traceback.extract_stack()) / 2) * " ", func.__name__))
return func(*args, **kwargs)
return inner
def log_class(cls):
for m in dir(cls):
if not m.startswith("__") and inspect.isfunction(getattr(cls, m)):
m = log_method(m)
print(m)
return cls
#log_class
class Cls:
def __init__(self):
pass
def A(self):
self.B()
def B(self):
self.C()
def C(self):
pass
Cls().A()
"""
Excepted output:
A
B
C
"""
You should rebind the method to the class object using setattr passing the method name m; assigning to the local name m like you've done, does nothing.
More so, you're currently passing m, a string, to log_method. Instead, you should pass the function object itself after retrieving via getattr:
def log_method(func):
def inner(*args, **kwargs):
print("{}{}".format(int(len(traceback.extract_stack()) / 2) * " ", func.__name__))
return func(*args, **kwargs)
return inner
def log_class(cls):
for m in dir(cls):
if not m.startswith("__") and inspect.isfunction(getattr(cls, m)):
setattr(cls, m, log_method(getattr(cls, m))) # here
return cls
Cls.A()
# A
# B
# C
PS: log_method is never used for decorating, so it's not a decorator.
Im just learning about classes in python and I would
like to know how results from a method in one class can
be used in a another class..here is the scenario.
Class A:
def x(self):
#do stuff and call Class B
Class B:
def y(self):
#do stuff get results and pass results to Class A
results
How can results be passed back to Class A ?
Thanks
maybe you want this
class A:
def x(self):
#do stuff and call Class B
n = 1
n += B().y() #Instance B and call y method
print n
class B:
def y(self):
#do stuff get results and pass results to Class A
x = 10
return x
a = A() #create a object with type A
a.x() #call x method of A class
Class A:
def x(self):
#do stuff and call Class B
my_B = B()
theResult = my_B.y() # you just called B's function y()
Class B:
def y(self):
#do stuff get results and pass results to Class A
return results
Assuming you create class B within the method A.x:
Class A:
def x(self):
#do stuff and call Class B
results = B().y()
Class B:
def y(self):
#do stuff get results and pass results to Class A
return results
Assuming method A.x gets the object of class B:
Class A:
def x(self, b):
#do stuff and call Class B
results = b.y()
Class B:
def y(self):
#do stuff get results and pass results to Class A
return results