I have the following code:
import threading
from functools import wraps
class Synchronized(object):
def __init__(self):
self.lock = threading.Lock()
def synchronized(f):
#wraps(f)
def wrapper(self, *args, **kwargs):
with self.lock:
print "here"
return f(self, *args, **kwargs)
return wrapper
#synchronized
def go(self):
print 1
class B(Synchronized):
#synchronized
def foo(self):
return 1
This code fails on import complaining:
File "a.py", line XXX, in B
#synchronized
NameError: name 'synchronized' is not defined
However if I comment out B and just use Syncrhonized().go() it works great.
Question: How does python know what is #synchronized in base class but fails to resolve it in its derivatives?
synchronized is defined as a function in the class body of Synchronized only.
A class body is executed like a function to define the class; the resulting local namespace is used to form the class attributes. This is why synchronized is still a local name within Synchronized when used as a decorator on go. You could compare that with defining the decorator inside a function, then trying to apply it outside of that function; it won't work, because the decorator is a local variable.
You could use #Syncronized.synchronized.im_func in class B (.im_func to unwrap the function from the method wrapper):
class B(Synchronized):
#Synchronized.synchronized.im_func
def foo(self):
return 1
Better still, do not defined the decorator inside a class at all but instead defined it outside of Synchronized. It is not a method, after all:
def synchronized(f):
#wraps(f)
def wrapper(self, *args, **kwargs):
with self.lock:
print "here"
return f(self, *args, **kwargs)
return wrapper
class Synchronized(object):
def __init__(self):
self.lock = threading.Lock()
#synchronized
def go(self):
print 1
class B(Synchronized):
#synchronized
def foo(self):
return 1
Related
I'm trying to apply a conditional decorator as described in another stackoverflow post, but I'd like the condition to be set from inside the class its being used. Instead I get a Reference error pointing that self is not defined.
class foo:
def __init__(self):
self.debug = True
#conditional_decorator(decorator, self.debug)
def function(self):
pass
I tried defining a global variable and updating it from inside the __init__() method but it kept its original value when called as an argument of the decorator.
debug = None
class foo:
def __init__(self):
self.debug = True
global debug
debug = self.debug
#conditional_decorator(decorator, debug)
def function(self):
pass
The only way it worked was declaring a global variable and setting it outside of the class.
How can I apply the value of the class property to the decorator?
An update to the answer given by #Maurice Meyer which allows a member of the class to be nominated:
from functools import wraps
def conditional_decorator(decoration, member):
def decorator(method):
predecorated = decoration(method)
#wraps(method)
def wrapper(*args, **kwargs):
self = args[0]
condition = getattr(self, member)
if not condition:
return method(*args, **kwargs)
return predecorated(*args, **kwargs)
return wrapper
return decorator
#And used like this for example:
class foo:
def __init__(self, debug):
self.debug = debug
#conditional_decorator(decorator, "debug")
def function(self):
pass
f1 = foo(True)
f1.function()
This is how you make a decorator handle classes and arguments:
from functools import wraps
def conditional_decorator(param):
def real_decorator(fn):
#wraps(fn)
def wrapper(*args, **kw):
cls = args[0]
print(cls.debug)
print(param)
return wrapper
return real_decorator
class foo:
def __init__(self):
self.debug = True
#conditional_decorator('param1')
def function(self):
pass
f = foo()
f.function()
Output:
True
param1
The decorator should not be conditional. Rather, when the decorated function is called, it should look at self.debug to determine whether to use the original function or the wrapped part.
def conditional_decorator(dec):
def decorator(func):
def _(self, *args, **kwargs):
f = func
if self.debug:
f = dec(f)
return f(self, *args, **kwargs)
return _
return decorator
def decorator(f):
def _(*args, **kwargs):
print("Decorated")
return f(*args, **kwargs)
return _
class foo:
def __init__(self, debug):
self.debug = debug
#conditional_decorator(decorator)
def function(self):
print("foo stuff")
foo(True).function()
print("===")
foo(False).function()
outputs
Decorated
foo stuff
===
foo stuff
In the code below I created a decorator to my class Class methods. I noticed that this decorator is called even without creating a class instance!
And without calling these methods in the class!
Is there any explanation for that?
decorator :
def deco(class_name):
def inner_function(method):
print("method is = {} and class is: {}".format(method.__name__,class_name.__name__))
return method
return inner_function
class_deco
class class_deco :
def __init__(self):
pass
Class:
class Class :
def __init__(self):
pass
#deco(class_deco)
def f1(self):
pass
#deco(class_deco)
def f2(self):
pass
When I run the script :
if __name__ == "__main__":
pass
I get this result:
method is = f1 and class is: class_deco
method is = f2 and class is: class_deco
Decorators are just syntatic sugar for the following
#deco(class_deco)
def f1(self):
pass
is same as -
f1 = deco(class_deco)(f1)
So this code runs as soon as the module is imported just like any other name declaration would and f1 name is replaced with decorated f1 as above.
As already explained, the #decorator syntax is only syntactic sugar, so this:
#somedecorator
def foo():
pass
is stryctly equivalent to
def foo():
pass
foo = somedecorator(foo)
In you case, you ARE explicitely calling the decorator function:
#deco(class_deco)
def f1(self):
pass
which is equivalent to:
def f1(self):
pass
_effective_decorator = deco(class_deco)
f1 = _effective_decorator(f1)
which is why your inner_function is indeed executed at import time.
Decorators that take additional params needs one more level of nesting, so technically your decorator should look like:
def deco(cls):
def real_deco(func):
def inner_function(*args, **kw):
print("method is = {} and class is: {}".format(func.__name__,cls.__name__))
return func(*args, **kw)
return inner_function
return real_deco
return inner_function
BUT if the point is to get the name of the class the method really belongs to, this is still broken - you should get the class from the instance on which the method is called, not try to hard-code it in the decorator call (which will never work as intended since the real class doesn't exist when you're applying the decorator to the function). So the proper implementation would look something like:
def deco(func):
# we're only supposed to use this on methods...
def wrapper(self, *args, **kw):
print("class {} - method {}".format(type(self).__name__, func.__name__))
return wrapper
class Class:
#deco
def f1(self):
pass
NB: this won't handle classmethods nor staticmethods, of course.
Here is a demo showing just two possible ways a decorator could be constructed:
def Deco(*deco_params):
print('In Deco', deco_params)
def deco(func):
print('In deco(func)')
def inner(*args, **kwargs):
print('In inner(*args, **kwargs)')
return func(*args, **kwargs)
return inner
return deco
def deco(method):
print('In deco(method)')
def inner_function(*args, **kwargs):
print("method is = {} called".format(method.__name__))
return method(*args, **kwargs)
return inner_function
class Class :
def __init__(self):
pass
#deco
def f1(self):
pass
#Deco(42)
def f2(self):
pass
if __name__ == "__main__":
print('Now in Main')
c = Class()
c.f1()
c.f2()
Output:
In deco(method)
In Deco (42,)
In deco(func)
Now in Main
method is = f1 called
In inner(*args, **kwargs)
This is a question similar to How to call a method implicitly after every method call? but for python
Say I have a crawler class with some attributes (e.g. self.db) with a crawl_1(self, *args, **kwargs) and another one save_to_db(self, *args, **kwargs) which saves the crawling results to a database (self.db).
I want somehow to have save_to_db run after every crawl_1, crawl_2, etc. call. I've tried making this as a "global" util decorator but I don't like the result since it involves passing around self as an argument.
If you want to implicitly run a method after all of your crawl_* methods, the simplest solution may be to set up a metaclass that will programatically wrap the methods for you. Start with this, a simple wrapper function:
import functools
def wrapit(func):
#functools.wraps(func)
def _(self, *args, **kwargs):
func(self, *args, **kwargs)
self.save_to_db()
return _
That's a basic decorator that wraps func, calling
self.save_to_db() after calling func. Now, we set up a metaclass
that will programatically apply this to specific methods:
class Wrapper (type):
def __new__(mcls, name, bases, nmspc):
for attrname, attrval in nmspc.items():
if callable(attrval) and attrname.startswith('crawl_'):
nmspc[attrname] = wrapit(attrval)
return super(Wrapper, mcls).__new__(mcls, name, bases, nmspc)
This will iterate over the methods in the wrapped class, looking for
method names that start with crawl_ and wrapping them with our
decorator function.
Finally, the wrapped class itself, which declares Wrapper as a
metaclass:
class Wrapped (object):
__metaclass__ = Wrapper
def crawl_1(self):
print 'this is crawl 1'
def crawl_2(self):
print 'this is crawl 2'
def this_is_not_wrapped(self):
print 'this is not wrapped'
def save_to_db(self):
print 'saving to database'
Given the above, we get the following behavior:
>>> W = Wrapped()
>>> W.crawl_1()
this is crawl 1
saving to database
>>> W.crawl_2()
this is crawl 2
saving to database
>>> W.this_is_not_wrapped()
this is not wrapped
>>>
You can see the our save_to_database method is being called after
each of crawl_1 and crawl_2 (but not after this_is_not_wrapped).
The above works in Python 2. In Python 3, replase this:
class Wrapped (object):
__metaclass__ = Wrapper
With:
class Wrapped (object, metaclass=Wrapper):
Something like this:
from functools import wraps
def my_decorator(f):
#wraps(f)
def wrapper(*args, **kwargs):
print 'Calling decorated function'
res = f(*args, **kwargs)
obj = args[0] if len(args) > 0 else None
if obj and hasattr(obj, "bar"):
obj.bar()
return wrapper
class MyClass(object):
#my_decorator
def foo(self, *args, **kwargs):
print "Calling foo"
def bar(self, *args, **kwargs):
print "Calling bar"
#my_decorator
def example():
print 'Called example function'
example()
obj = MyClass()
obj.foo()
It will give you the following output:
Calling decorated function
Called example function
Calling decorated function
Calling foo
Calling bar
A decorator in Python looks like this, it's a method taking a single method as argument and returning another wrapper method that shall be called instead of the decorated one. Usually the wrapper "wraps" the decorated method, i.e. calls it before/after performing some other actions.
Example:
# define a decorator method:
def save_db_decorator(fn):
# The wrapper method which will get called instead of the decorated method:
def wrapper(self, *args, **kwargs):
fn(self, *args, **kwargs) # call the decorated method
MyTest.save_to_db(self, *args, **kwargs) # call the additional method
return wrapper # return the wrapper method
Now learn how to use it:
class MyTest:
# The additional method called by the decorator:
def save_to_db(self, *args, **kwargs):
print("Saver")
# The decorated methods:
#save_db_decorator
def crawl_1(self, *args, **kwargs):
print("Crawler 1")
#save_db_decorator
def crawl_2(self, *args, **kwargs):
print("Crawler 2")
# Calling the decorated methods:
my_test = MyTest()
print("Starting Crawler 1")
my_test.crawl_1()
print("Starting Crawler 1")
my_test.crawl_2()
This would output the following:
Starting Crawler 1
Crawler 1
Saver
Starting Crawler 1
Crawler 2
Saver
See this code running on ideone.com
Can you create a decorator inside a class that will see the classes methods and variables?
The decorator here doesnt see: 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)
There's no real need to implement this decorator as a class, and there's no need to implement it inside the definition of the Foo class. The following will suffice:
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
Using that decorator seems to work:
>>> 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
You can have it be a class but you need to use the descriptor protocol
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)
You always need to use a descriptor when you want to decorate class methods with a class instance using the __call__ method. The reason for this is that there will only be one self passed in which refers to the instance of the decorating class and not the instance of the decorated method.
My previous answer was made in haste. If you're wanting to write a decorator you should really use wraps from the functools module. It takes care of the hard stuff for you.
A proper way to define the canRun decorator is:
from functools import wraps
def canRun(f):
#wraps(f)
def wrapper(instance, *args):
if instance.longcondition():
return f(instance, *args)
return wrapper
The canRun function should be defined outside of the class.
Here's an example of what I mean:
class MyDecorator(object):
def __call__(self, func):
# At which point would I be able to access the decorated method's parent class's instance?
# In the below example, I would want to access from here: myinstance
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
class SomeClass(object):
##self.name = 'John' #error here
name="John"
#MyDecorator()
def nameprinter(self):
print(self.name)
myinstance = SomeClass()
myinstance.nameprinter()
Do I need to decorate the actual class?
class MyDecorator(object):
def __call__(self, func):
def wrapper(that, *args, **kwargs):
## you can access the "self" of func here through the "that" parameter
## and hence do whatever you want
return func(that, *args, **kwargs)
return wrapper
Please notice in this context that the use of "self" is just a convention, a method just uses the first argument as a reference to the instance object:
class Example:
def __init__(foo, a):
foo.a = a
def method(bar, b):
print bar.a, b
e = Example('hello')
e.method('world')
The self argument is passed as the first argument. Also your MyDecorator is a class emulating a function. Easier to make it an actual function.
def MyDecorator(method):
def wrapper(self, *args, **kwargs):
print 'Self is', self
return method(self, *args, **kwargs)
return wrapper
class SomeClass(object):
#MyDecorator
def f(self):
return 42
print SomeClass().f()