I want to use a decorator to do some preparation job and record the status the function have, so I write something like that:
class Decorator:
def __init__(self, func):
self.count = 0
self.func = func
def __call__(self, *args, **kwargs):
self.count += 1 # Simply count the call times
return self.func(self, *args, **kwargs)
class Foo:
def __init__(self):
self.value = 0
#Decorator
def test(self, value):
self.value = value # change the value of instance
print(self.value)
f = Foo()
f.test(1)
print(f.value)
print(f.test.value)
But it's obvious that self in __call__(self, *args, **kwargs) corresponds to instance of Decorator instead of the instance of Foo , which will make f.value unchanged but f.test.value increase .
Is there any way I can pass the instance of Foo to Decorator instead of Decorator itself?
Or is there any way to implement this function much more clear?
As the decorator is only called once and replaces the method for all instance with one instance of the Decorator class. All it does is:
Foo.test = Decorator(Foo.test)
This makes it impossible to detect the instance called. One work-around would be to apply the decorator in the __init__ of Foo by hand:
class Foo:
def __init__(self):
self.value = 0
self.test = Decorator(self.test)
def test(self, value):
self.value = value # change the value of instance
print(self.value)
This way the decorator wraps the instance method, so you do not need to pass self in the __call__ of Decorator:
class Decorator:
def __init__(self, func):
self.count = 0
self.func = func
def __call__(self, *args, **kwargs):
self.count += 1 # Simply count the call times
return self.func(*args, **kwargs)
Now it works and you have to update you test method, as f.test.value no longer exists:
f = Foo()
f.test(1)
print(f.value)
It outputs two times a 1 as expected.
I got this here
import functools
class Decorator(object):
def __init__(self, func):
self.count = 0
self.func = func
def __call__(self, *args, **kwargs):
self.count += 1 # Simply count the call times
return self.func( *args, **kwargs)
def __get__(self, instance, instancetype):
"""Implement the descriptor protocol to make decorating instance
method possible.
"""
# Return a partial function with the first argument is the instance
# of the class decorated.
return functools.partial(self.__call__, instance)
class Foo:
def __init__(self):
self.value = 0
#Decorator
def test(self, value):
self.value = value # change the value of instance
f = Foo()
f.test(3)
print(f.value) # prints 3
g = Foo()
g.test(8)
print(g.value) # prints 8
or
May be this
def preJob(function):
def updateToDo(self, *args, **kwargs):
# do some recording
function(self, *args, **kwargs)
return updateToDo
class Foo(object):
def __init__(self):
self.value = 0
#preJob
def test(self, value):
self.value = value
f = Foo()
f.test(3)
print(f.value) # prints 3
g = Foo()
g.test(8)
print(g.value) # prints 8
class threadSafeGenerator(object):
"""docstring for threadSafeGenerator"""
class SafeGenerator(object):
"""docstring for SafeGenerator"""
def __init__(self, iterable):
self.iterable = iterable
self.lock = Lock()
def __iter__(self):
return self
def __next__(self):
with self.lock:
return next(self.iterable)
def __init__(self, func):
super(threadSafeGenerator, self).__init__()
self.func = func
def __call__(self, *args, **kwargs):
return self.SafeGenerator(self.func(self, *args, **kwargs))
I found using Priyesh Kumar's answer that you can simply pass the self argument from the call method to the function being decorated:
def __call__(self, *args, **kwargs):
return self.SafeGenerator(self.func(self, *args, **kwargs))
hope this helps!
EDIT:
Never mind only works if the function being passed through the decorator does not call class variables defined in the init method
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
I've subclassed property to try to add a lock to it so access to it is atomic(only one thread can read and write to it at one time). But I'm running into errors with the initializer and can't figure out why.
To be clear: I don't want to make a descriptor, I want to be able to use #atomic_property decorator instead of #property
class atomic_property(property):
def __init__(self, func, name=None, doc=None):
self.__name__ = name or func.__name__
self.__module__ = func.__module__
self.__doc__ = doc or func.__doc__
self.func = func
self.lock = threading.Lock()
def __get__(self, obj, objtype=None):
with self.lock:
return self.func(obj)
def __set__(self, obj, value):
with self.lock:
return self.func(obj, value)
Here is the traceback from ipython.
In [12]: class a(object):
def __init__(self):
self._b = 1
#utils.atomic_property
def b(self):
return self._b
#b.setter
def b(self, val):
self._b = val
....:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-12-daec89385bc3> in <module>()
----> 1 class a(object):
2 def __init__(self):
3 self._b = 1
4 #utils.atomic_property
5 def b(self):
<ipython-input-12-daec89385bc3> in a()
5 def b(self):
6 return self._b
----> 7 #b.setter
8 def b(self, val):
9 self._b = val
TypeError: __init__() takes from 2 to 4 positional arguments but 5 were given
You are far from properly subclassing property with your code: for once
you rewrite some methods, but never care to call the upstream method so it can do its business.
Not that it can't be done this way, if you actually reproduce in your subclass all behaviors the methods on the superclass would perform - but even without looking at the code of the superclass (which in cPython's case is in native code) - one can see that either self.func is the getter function, or it is the setter function - it can't be both.
If you just care to annotate the attributes you will use and perform the actions you want, and delegate the remainder to the original code, it will very
likely just work:
class atomic_property(property):
def __init__(self,*args, **kw):
super(atomic_property, self).__init__(*args, **kw)
self.lock = threading.Lock()
def __get__(self, obj, obj_type):
with self.lock:
return super(atomic_property, self).__get__(obj, objtype)
def __set__(self, obj, value):
with self.lock:
return super(atomic_property, self).__set__(obj, value)
It seems that using property.setter calls the __init__ again, which seems odd (initialize the same object more then once?)
Anyway you need to let property do it's job, how do you expect it to work correctly if you don't use property.__init__ at all? You could do something like this for example:
class atomic_property(property):
def __init__(self, *args, **kwargs):
#print(args,kwargs) #uncomment to see this be called again when using .setter !
super(atomic_property,self).__init__(*args,**kwargs)
self.lock = threading.Lock()
def __get__(self, obj, objtype=None):
with self.lock:
return super(atomic_property,self).__get__(obj,objtype)
def __set__(self, obj, value):
with self.lock:
return super(atomic_property,self).__set__(obj,value)
Lets assume I've a class A which has a bunch of methods, but I want it to run certain lines before and after each method is called.
For example: I want my class Dog here to run before() and after() every time bark() or run() are been called.
class Dog():
def __init__(self, sound, speed):
self.sound = sound
self.speed = speed
def before(self):
check_some_things(self)
def after(self):
do_some_things(self)
def bark(self):
sound(self.sound)
def run(self):
move(self.speed)
You could encapsulate this in a decorator; the following decorator will call before and after if these are available on self:
import inspect
from functools import wraps
def before_and_after(f):
#wraps(f)
def wrapper(self, *args, **kw):
if hasattr(self, 'before') and inspect.ismethod(self.before):
self.before()
result = f(self, *args, **kw)
if hasattr(self, 'after') and inspect.ismethod(self.after):
self.after()
return result
return wrapper
then simply apply to the methods that should be wrapped:
class Dog():
def __init__(self, sound, speed):
self.sound = sound
self.speed = speed
def before(self):
check_some_things(self)
def after(self):
do_some_things(self)
#before_and_after
def bark(self):
sound(self.sound)
#before_and_after
def run(self):
move(self.speed)
The decorator assumes it is used on methods, e.g. the produced wrapper expects self as a first argument.
If this needs to apply to all methods that are not before or after, perhaps a metaclass is in order:
class BeforeAfterMeta(type):
def __new__(mcs, classname, bases, body):
for name, value in body.items():
if not inspect.isfunction(value):
continue
if name in ('before', 'after') or name[:2] + name[-2:] == '_' * 4:
# before or after hook, or a special method name like __init__.
continue
body[name] = before_and_after(value)
return super(BeforeAfterMeta, mcs).__new__(mcs, classname, bases, body)
which you then can apply to your class:
class Dog(metaclass=BeforeAfterMeta):
def __init__(self, sound, speed):
self.sound = sound
self.speed = speed
def before(self):
check_some_things(self)
def after(self):
do_some_things(self)
def bark(self):
sound(self.sound)
def run(self):
move(self.speed)
You could also use a decorator function to inspect your class Dog if the pre and post methods exists and override the run method:
def PrePostMethod(inputClass):
mainRun = inputClass.run
beforeFunc = inputClass.before if "before" in inputClass.__dict__ else None
afterFunc = inputClass.after if "after" in inputClass.__dict__ else None
def new_run(self, *args, **kwargs):
# you could inspect the given arguments if you need
# to parse arguments into before and the after methods
if beforeFunc:
self.before()
mainRun(self)
if afterFunc:
self.after()
inputClass.run = new_run
return inputClass
#PrePostMethod
class Dog(object):
def __init__(self, sound, speed):
self.sound = sound
self.speed = speed
def before(self):
print "Do stuff before"
def after(self):
print "Do stuff after"
def run(self):
print "Do main process"
Dog(1,2).run()
To parse arguments and keywords arguments from run into before and after, use the class inspect and loop through the args and kwargs to parse the right ones.
from inspect import getargspec
def argHandler(method, *args, **kwargs):
method = getargspec(method)
mArgs = method.args
mKwargs = method.keywords
rArgs = args[:len(mArgs)-1]
rKwargs = { k:v for k,v in kwargs.iteritems() if k in mKwargs }
leftArgs = len(mArgs)-len(rArgs)
if len(rKwargs):
rKwargs = [ rKwargs[k] for k in mArgs[:leftArgs-1]]
rArgs += rKwargs
return rArgs
def PrePostMethod(inputClass):
mainRun = inputClass.run
beforeFunc = inputClass.before if "before" in inputClass.__dict__ else None
afterFunc = inputClass.after if "after" in inputClass.__dict__ else None
def new_run(self, *args, **kwargs):
if beforeFunc:
nargs = argHandler(self.before, *args, **kwargs)
if nargs: self.before( *nargs)
else: self.before()
nargs = argHandler(mainRun, *args, **kwargs)
if nargs: mainRun(self, *nargs)
else: mainRun(self)
if afterFunc:
nargs = argHandler(self.after, *args, **kwargs)
if nargs: self.after( *nargs)
else: self.after()
inputClass.run = new_run
return inputClass
You can use many different ways to do this. But I think the best way is, to define a class with the Pre- and Post-Methods and redefine it's object hidden methods: __enter__ and __exit__. To use them, just call the class with the compound statement with.
class pre_post(object):
def __enter__(self):
print "Enter check method.."
def __exit__(self, type, value, tb):
print "Exit check method.."
class dog(object):
def run(self, checkups=True):
if checkups:
with pre_post() as pp:
print "My stuff.."
else:
print "My stuff.."
dog().run(True)
This will give you the following result:
Enter check method..
My stuff..
Exit check method..
I hope that will help you.
I have a decorator declared as a class:
class predicated(object):
def __init__(self, fn):
self.fn = fn
self.fpred = lambda *args, **kwargs: True
def predicate(self, predicate):
self.fpred = predicate
return self
def validate(self, *args, **kwargs):
return self.fpred(*args, **kwargs)
def __call__(self, *args, **kwargs):
if not self.validate(*args, **kwargs):
raise PredicateNotMatchedError("predicate was not matched")
return self.fn(*args, **kwargs)
... and when I use it to wrap a method in a class, calling that method does not seem to set the instance of the object as the first argument. While this behavior is not exactly unexpected, how would I go about getting self to be frozen when the method becomes an instance method?
Simplified example:
class test_decorator(object):
def __init__(self, fn):
self.fn = fn
def __call__(self, *args, **kwargs):
return self.fn(*args, **kwargs)
class Foo(object):
#test_decorator
def some_method(self):
print(self)
Foo().some_method()
Expected instance of foo, instead get an error saying 0 arguments were passed.
Figured it out - needed to define a __get__ method in order to create a MethodType binding like so:
def __get__(self, obj, objtype=None):
return MethodType(self, obj, objtype)
which creates a MethodType object when invoking the method on an object that freezes the self argument.
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()