Is there a clean way to have a decorator call an instance method on a class only at the time an instance of the class is instantiated?
class C:
def instance_method(self):
print('Method called')
def decorator(f):
print('Locals in decorator %s ' % locals())
def wrap(f):
print('Locals in wrapper %s' % locals())
self.instance_method()
return f
return wrap
#decorator
def function(self):
pass
c = C()
c.function()
I know this doesn't work because self is undefined at the point decorator is called (since it isn't called as an instance method as there is no available reference to the class). I then came up with this solution:
class C:
def instance_method(self):
print('Method called')
def decorator():
print('Locals in decorator %s ' % locals())
def wrap(f):
def wrapped_f(*args):
print('Locals in wrapper %s' % locals())
args[0].instance_method()
return f
return wrapped_f
return wrap
#decorator()
def function(self):
pass
c = C()
c.function()
This uses the fact that I know the first argument to any instance method will be self. The problem with the way this wrapper is defined is that the instance method is called every time the function is executed, which I don't want. I then came up with the following slight modification which works:
class C:
def instance_method(self):
print('Method called')
def decorator(called=[]):
print('Locals in decorator %s ' % locals())
def wrap(f):
def wrapped_f(*args):
print('Locals in wrapper %s' % locals())
if f.__name__ not in called:
called.append(f.__name__)
args[0].instance_method()
return f
return wrapped_f
return wrap
#decorator()
def function(self):
pass
c = C()
c.function()
c.function()
Now the function only gets called once, but I don't like the fact that this check has to happen every time the function gets called. I'm guessing there's no way around it, but if anyone has any suggestions, I'd love to hear them! Thanks :)
I came up with this as a possible alternative solution. I like it because there is only one call that happens when the function is defined, and one when the class is instantiated. The only downside is a tiny bit of extra memory consumption for the function attribute.
from types import FunctionType
class C:
def __init__(self):
for name,f in C.__dict__.iteritems():
if type(f) == FunctionType and hasattr(f, 'setup'):
self.instance_method()
def instance_method(self):
print('Method called')
def decorator(f):
setattr(f, 'setup', True)
return f
#decorator
def function(self):
pass
c = C()
c.function()
c.function()
I think you're asking something fundamentally impossible. The decorator will be created at the same time as the class, but the instance method doesn't exist until the instance does, which is later. So the decorator can't handle instance-specific functionality.
Another way to think about this is that the decorator is a functor: it transforms functions into other functions. But it doesn't say anything about the arguments of these functions; it works at a higher level than that. So invoking an instance method on an argument of function is not something that should be done by a decorator; it's something that should be done by function.
The way to get around this is necessarily hackish. Your method looks OK, as hacks go.
It can be achieved by using callables as decorators.
class ADecorator(object):
func = None
def __new__(cls, func):
dec = object.__new__(cls)
dec.__init__(func)
def wrapper(*args, **kw):
return dec(*args, **kw)
return wrapper
def __init__(self, func, *args, **kw):
self.func = func
self.act = self.do_first
def do_rest(self, *args, **kw):
pass
def do_first(self, *args, **kw):
args[0].a()
self.act = self.do_rest
def __call__(self, *args, **kw):
return self.act(*args, **kw)
class A(object):
def a(self):
print "Original A.a()"
#ADecorator
def function(self):
pass
a = A()
a.function()
a.function()
How should multiple instances of the class C behave? Should instance_method only be called once, no matter which instance calls function? Or should each instance call instance_method once?
Your called=[] default argument makes the decorator remember that something with string name function has been called. What if decorator is used on two different classes which both have a method named function? Then
c=C()
d=D()
c.function()
d.function()
will only call c.instance_method and prevent d.instance_method from getting called. Strange, and probably not what you want.
Below, I use self._instance_method_called to record if self.instance_method has been called. This makes each instance of C call instance_method at most once.
If you want instance_method to be called at most once regardless of which instance of C calls function, then simply define _instance_method_called as a class attribute instead of an instance attribute.
def decorator():
print('Locals in decorator %s ' % locals())
def wrap(f):
def wrapped(self,*args):
print('Locals in wrapper %s' % locals())
if not self._instance_method_called:
self.instance_method()
self._instance_method_called=True
return f
return wrapped
return wrap
class C:
def __init__(self):
self._instance_method_called=False
def instance_method(self): print('Method called')
#decorator()
def function(self):
pass
c = C()
# Locals in decorator {}
c.function()
# Locals in wrapper {'self': <__main__.C instance at 0xb76f1aec>, 'args': (), 'f': <function function at 0xb76eed14>}
# Method called
c.function()
# Locals in wrapper {'self': <__main__.C instance at 0xb76f1aec>, 'args': (), 'f': <function function at 0xb76eed14>}
d = C()
d.function()
# Locals in wrapper {'self': <__main__.C instance at 0xb76f1bcc>, 'args': (), 'f': <function function at 0xb76eed14>}
# Method called
d.function()
# Locals in wrapper {'self': <__main__.C instance at 0xb76f1bcc>, 'args': (), 'f': <function function at 0xb76eed14>}
Edit: To get rid of the if statement:
def decorator():
print('Locals in decorator %s ' % locals())
def wrap(f):
def rest(self,*args):
print('Locals in wrapper %s' % locals())
return f
def first(self,*args):
print('Locals in wrapper %s' % locals())
self.instance_method()
setattr(self.__class__,f.func_name,rest)
return f
return first
return wrap
class C:
def instance_method(self): print('Method called')
#decorator()
def function(self):
pass
Related
How can I decorate the last function in a class inheritance?
If I decorate a superclass function, the subclass function overrides the decorator.
I'd like to find out if there is a neat way to automatically decorate the top function in the MRO.
def wrapper(f):
def _wrap(*args, **kwargs):
print("In wrapper")
return f(*args, **kwargs)
return _wrap
class A:
#wrapper
def f(self):
print("In class A")
class B(A):
def f(self):
print("In class B")
if __name__ == '__main__':
a = A()
b = B()
print("Calling A:")
a.f()
print("Calling B:")
b.f()
Here is the output. As expected, B.f() does not call the wrapper, though I'd like it to.
Calling A:
In wrapper
In class A
Calling B:
In class B
Here is what I have tried thus far. A metaclass that holds all the decorators and injects them during class instantiation.
from abc import ABCMeta
class WrapperMetaClass(ABCMeta):
def __init__(cls, *args, **kwargs):
wrappers_dict = getattr(cls, "_wrappers")
for attr_name in dir(cls):
if attr_name not in wrappers_dict:
continue
else:
wrapper = wrappers_dict[attr_name]
attr = getattr(cls, attr_name)
if not hasattr(attr, '__call__'):
raise Exception("What you're trying to wrap is not a function!")
attr = wrapper(attr)
setattr(cls, attr_name, attr)
super().__init__(*args, **kwargs)
This works:
class A(metaclass=WrapperMetaClass):
_wrappers = {
"f": wrapper
}
def f(self):
print("In class A")
class B(A):
def f(self):
print("In class B")
The output is what I wanted.
Calling A:
In wrapper
In class A
Calling B:
In wrapper
In class B
However, this runs into a different issue. If B does not override f, the metaclass wraps A.f() twice. This makes sense, as both A and B inherit WrapperMetaClass, so A.f() is wrapped first, and then B.f() is wrapped again.
class A(metaclass=WrapperMetaClass):
_wrappers = {
"f": wrapper
}
def f(self):
print("In class A")
class B(A):
pass
The output becomes:
Calling A:
In wrapper
In class A
Calling B:
In wrapper
In wrapper
In class A
I have no idea what else I could do.
Yes, I remember facing this once or twice - and you are on the right track.
But first things first: if the logic in your "wrapper" is something that
could be put in a method in the base class, then breaking-up the methods
in smaller-tasks, and have a "method slot" system is preferable to this,
as user 2357112 supports monica puts in the comments. If you find out you really need or prefer decorators, the full code is bellow
class A:
def do_things(self):
create_connection() # <- this is the code you'd are putting in the wrapper in the other approach
do_thing_1()
class B(A):
def do_things(self):
# here we have to do thing_1 and thing_2, but
# the connection is created in the superclass method...
# this could be the origin of your question
# Refactor to:
class A:
def do_things(self):
create_connection()
self.internal_do_things()
def internal_do_things(self):
do_thing_1()
class B(A):
def internal_do_things(self):
super().internal_do_things()
do_thing_2()
So, classical inheritance and OO solves this
If you need the decorators anway:
The thing to do is to have the decorator itself, the "wrapper", get
a way to "know" if it already was called in an outer method (i.e. a method
in a subclass which calls super()), and just act as a transparent
wrapper in this case.
It gets a bit further complicated when we want a robust solution:
a wrapper that can work for different methods in the same class,
and does not get confused if they are called concurrently
(in different threads, or a method calling another method,
not super(), which should trigger the wrapper).
And in the end, the mechanisms for that are complicated enough that
they should not get in the way of your actual wrapper - so,
ideally they should be built as a decorator themselves, which will
decorate your wrapper.
[hours later]
So, sorry if it does not look "neat" - it turns out implementing what is described above is a bit more complex than I thought initially - we need an intermediate decorator level (called meta_wrapper_applier in the code), so that the metaclass can re-wrap the methods each time they are redeclared.
I hope the comments in the code and variable names are enough to understand the idea:
from abc import ABCMeta
from functools import wraps
import threading
class WrapperMetaClass(ABCMeta):
def __init__(cls, name, bases, ns, **kw):
super().__init__(name, bases, ns, **kw)
# Get the wrapped methods for all the superclasses
super_wrappers = {}
for supercls in cls.__mro__[::-1]:
super_wrappers.update(supercls.__dict__.get("_wrappers", {}))
# unconditionally install a wrappers dict for each subclass:
sub_wrappers = cls._wrappers = {}
for attrname, attr in ns.items():
if attrname in super_wrappers:
# Applies the wrapper in the baseclass to the subclass method:
setattr(cls, attrname, super_wrappers[attrname]._run_once_wrapper(attr))
elif hasattr(attr, "_run_once_wrapper"):
# Store the wrapper information in the cls for use of the subclasses:
sub_wrappers[attrname] = attr
def run_once_method_decorator(original_wrapper):
re_entering_stacks = {}
# This is the callable used to place a wrapper on the original
# method and on each overriden method.
# All methods with the same name in the subclasses will share the same original wrapper and the
# "re_entering_stacks" data structure.
def meta_wrapper_applier(raw_method):
wrapped_method_in_subclass = None
#wraps(original_wrapper)
def meta_wrapper(*args, **kw):
nonlocal wrapped_method_in_subclass
# uses a plain list to keep track of re-entering the same-named method
# in each thread:
re_entering_stack = re_entering_stacks.setdefault(threading.current_thread(), [])
re_entering = bool(re_entering_stack)
try:
re_entering_stack.append(1)
if re_entering:
result = raw_method(*args, **kw)
else:
if wrapped_method_in_subclass is None:
# Applies the original decorator lazily, and caches the result:
wrapped_method_in_subclass = original_wrapper(raw_method)
result = wrapped_method_in_subclass(*args, **kw)
finally:
re_entering_stack.pop()
return result
# registry = original_wrapper.__dict__.setdefault("_run_once_registry", {})
meta_wrapper._run_once_wrapper = meta_wrapper_applier
return meta_wrapper
return meta_wrapper_applier
# From here on, example code only;
#run_once_method_decorator
def wrapper(f):
#wraps(f)
def _wrap(*args, **kwargs):
print("Entering wrapper")
result = f(*args, **kwargs)
print("Leaving wrapper\n")
return result
return _wrap
#run_once_method_decorator
def other_wrapper(f):
#wraps(f)
def _wrap(*args, **kwargs):
print("Entering other wrapper")
result = f(*args, **kwargs)
print("Leaving other wrapper\n")
return result
return _wrap
class A(metaclass=WrapperMetaClass):
#wrapper
def f(self):
print("In class A")
def g(self):
print("g in A")
class B(A):
def f(self):
print("In class B")
super().f()
#other_wrapper
def g(self):
print("g in B")
super().g()
class C(B):
def g(self):
print("g in C")
super().g()
if __name__ == '__main__':
a = A()
b = B()
print("Calling A:")
a.f()
a.g()
print("Calling B:")
b.f()
b.g()
print("Calling C:")
C().g()
Output:
Calling A:
Entering wrapper
In class A
Leaving wrapper
g in A
Calling B:
Entering wrapper
In class B
In class A
Leaving wrapper
Entering other wrapper
g in B
g in A
Leaving other wrapper
Calling C:
Entering other wrapper
g in C
g in B
g in A
Leaving other wrapper
The following minimal example of a decorator on a member function:
def wrap_function(func):
def wrapper(*args, **kwargs):
print(args)
print(kwargs)
return wrapper
class Foo:
#wrap_function
def mem_fun(self, msg):
pass
foo = Foo()
foo.mem_fun('hi')
outputs:
(<__main__.Foo object at 0x7fb294939898>, 'hi')
{}
So self is one of the args.
However when using a wrapper class:
class WrappedFunction:
def __init__(self, func):
self._func = func
def __call__(self, *args, **kwargs):
print(args)
print(kwargs)
def wrap_function(func):
return WrappedFunction(func)
class Foo:
#wrap_function
def mem_fun(self, msg):
pass
foo = Foo()
foo.mem_fun('hi')
the output is:
('hi',)
{}
So the self, that references the Foo object, is not accessible in the body of __call__ of the WrappedFunction object.
How can I make it accessible there?
You're losing the reference to your bounded instance by wrapping the function logic (but not the instance) and redirecting it to a class instance - at that point, the class instance's own self applies instead of the wrapped instance method as it gets lost in the intermediary decorator (wrap_function()).
You either have to wrap the call to the wrapped function and pass *args/**kwargs to it, or just make a proper wrapper class instead of adding an intermediary wrapper:
class WrappedFunction(object):
def __call__(self, func):
def wrapper(*args, **kwargs):
print(args)
print(kwargs)
# NOTE: `WrappedFunction` instance is available in `self`
return wrapper
class Foo:
#WrappedFunction() # wrap directly, without an intermediary
def mem_fun(self, msg):
pass
foo = Foo()
foo.mem_fun('hi')
# (<__main__.Foo object at 0x000001A2216CDBA8>, 'hi')
# {}
Sadly, but this might be the only solution as you need it in the __call__ function.
Would suggest checking this out: What is the difference between __init__ and __call__ in Python?
def wrap_function(func):
def wrapper(*args, **kwargs):
x = WrappedFunction(func)
x(*args, **kwargs)
return wrapper
Wrapping a class's method in a "boilerplate" Python decorator will treat that method as a regular function and make it lose its __self__ attribute that refers to the class instance object. Can this be avoided?
Take the following class:
class MyClass(object):
def __init__(self, a=1, b=2):
self.a = a
self.b = b
def meth(self):
pass
If meth() is undecorated, MyClass().meth.__self__ refers to an instance method and enables something like setattr(my_class_object.meth.__self__, 'a', 5).
But when wrapping anything in a decorator, only the function object is passed; the object to which it is actually bound is not passed on along with it. (See this answer.)
import functools
def decorate(method):
#functools.wraps(method)
def wrapper(*args, **kwargs):
# Do something to method.__self__ such as setattr
print(hasattr(method, '__self__'))
result = method(*args, **kwargs)
return result
return wrapper
class MyClass(object):
def __init__(self, a=1, b=2):
self.a = a
self.b = b
#decorate
def meth(self):
pass
MyClass().meth()
# False <--------
Can this be overriden?
Your main misunderstanding here is order of operation.
When the decorate() decorator is called, meth() is not a method yet - it is still a function - it is only when the class block is over that meth is transformed into a method by the metaclass descriptors! - that's why it doesn't have __self__ (yet).
In other words, to decorate methods you have to ignore the fact that they are methods and treat them as normal functions - because that's what they are when the decorator is called.
In fact, the original meth function will never turn into a method - instead the function wrapper you returned from the decorator will be part of the class and will be the one that will get the __self__ attribute later.
If you decorate method of the class, first argument is always self object (you can access it with args[0]):
import functools
def decorate(method):
#functools.wraps(method)
def wrapper(*args, **kwargs):
print(hasattr(args[0], 'a'))
result = method(*args, **kwargs)
return result
return wrapper
class MyClass(object):
def __init__(self, a=1, b=2):
self.a = a
self.b = b
#decorate
def meth(self):
pass
MyClass().meth()
Prints:
True
Edit:
You can specify also self in your wrapper function (based on comments):
import functools
def decorate(method):
#functools.wraps(method)
def wrapper(self, *args, **kwargs):
print(hasattr(self, 'a'))
result = method(self, *args, **kwargs)
return result
return wrapper
class MyClass(object):
def __init__(self, a=1, b=2):
self.a = a
self.b = b
#decorate
def meth(self):
pass
MyClass().meth()
Prints also:
True
Let me clarify the process of decorating:
When you decorate meth with decorate in class MyClass, you are doing:
class MyClass(object):
... omit
meth = decorate(meth) # the meth in "()" is the original function.
As you can see, decorate takes method which is a function as parameter and return wrapper which is another funtion. And now the original meth in MyClass is replaced by new one wrapper. So when you call myclass_instance.meth(), you are calling the new wrapper function.
There isn't any black magic, so self can be definitely passed into wrapper, and it is safe to accept self using wrapper(self, *args, **kwargs).
This question already has answers here:
Attaching a decorator to all functions within a class
(11 answers)
Closed 5 years ago.
I have several classes and they have same implements name but difference realization. I want to decorate all methods in some classes but others not. I have thought about inheritance, but some classes have some methods do not need to be decorated. The problem is that I don't want to decorate methods one by one, some classes they need to be decorated by a same decorator, Is there any solution to fix it?
Your can start all method that required to be decorated with some prefix and then use something like this:
class Xobject(object):
def __init__(self, decorator):
for method_name in dir(self):
if method_name.startswith("dec_"):
attr = getattr(self, method_name)
wrapped = decorator(attr)
setattr(self, method_name, wrapped)
def dec_me_1(self):
print("In dec_me1")
return 0
def dec_me_2(self):
print("In dec_me2")
return 1
def decorator(func):
def wrapped(*args):
print("TEST")
return func(*args)
return wrapped
x = Xobject(decorator)
x.dec_me_1()
x.dec_me_2()
UPDATE:
You can decorate class by mean of function below. When using Python you should know that class in Python is also object so you could change it and pass it to the other function.
def decorator(func):
def wrapped(*args):
print("TEST")
return func(*args)
return wrapped
def decorate_object(p_object, decorator):
for method_name in dir(p_object):
if method_name.startswith("dec_"):
attr = getattr(p_object, method_name)
wrapped = decorator(attr)
setattr(p_object, method_name, wrapped)
decorate_object(Xobject, decorator)
x = Xobject()
x.dec_me_1()
x.dec_me_2()
Also your can decorate already instantiated object same way:
x = Xobject()
x.dec_me_1()
x.dec_me_2()
decorate_object(x, decorator)
x.dec_me_1()
x.dec_me_2()
I'm sure there are a few approaches to this, but the main leading options are:
Create a custom metaclass, where the __new__ method iterates across the attribute dictionary, identifies methods, and decorates them. See http://eli.thegreenplace.net/2011/08/14/python-metaclasses-by-example/ for an example of Python metaclass programming. Disadvantages: that may be more complex than we'd want to get into here.
Do the same in a regular class's __init__ method. Disadvantages: that only decorates instance methods and not class or static methods, and it's slower because it runs every time you create a new instance.
Do it outside the class:
class Foo(object):
def bar(self):
print 'bar'
for name, ref in vars(Foo):
if callable(ref): ...
Disadvantages: You only get one chance to do it right: at import time. Subclasses don't get modified.
Do it in a class-level decorator. Same disadvantages as doing it outside the class (I think).
At some point you have to be explicit about what gets wrapped and what doesn't.
If I've understood you correctly, I think you could do something like this:
def wrapper(func):
def inner(*args, **kwargs):
print "%s was called" func.__name__
return func(*args, **kwargs)
return inner
class A(object):
def foo(self):
print "foo called"
def bar(self):
print "BAR CALLED"
class B(A):
#wrapper
def foo(self):
super(B, self).foo()
class C(A):
#wrapper
def bar(self):
super(C, self).bar()
Stick = A()
Dave = B()
Jupiter = C()
Jupiter.foo() #prints "foo called"
Jupiter.bar() #prints "bar wrapped" and "BAR CALLED"
It seems that mymethod is not yet a method when the decorator is called.
import inspect
class decorator(object):
def __call__(self, call):
if inspect.ismethod(call): #Not working yet
obj = "method"
args = inspect.getargspec(call)[0][1:]
elif inspect.isfunction(call):
obj = "function"
args = inspect.getargspec(call)[0]
elif inspect.isclass(call):
obj = "class"
args = inspect.getargspec(call.__init__)[0][1:]
args="(%s)" % repr(args)[1:-1].replace("'","")
print "Decorate %s %s%s" % (obj, call.__name__, args)
return call
#decorator()
def myfunction (a,b): pass
#decorator()
class myclass():
def __init__(self, a, b): pass
#decorator()
def mymethod(self, a, b): pass
if inspect.isfunction(myclass.mymethod):
print "mymethod is a function"
if inspect.ismethod(myclass.mymethod):
print "mymethod is a method"
Output:
Decorate function myfunction(a, b)
Decorate function mymethod(self, a, b)
Decorate class myclass(a, b)
mymethod is a method
I would know if the first argument is 'self', but there will be a less dirty solution?
Edit: Why?
I want to populate a list of callables and their arguments, if it is a function or a class, and I can pass the expected arguments, then I call it, but if it is a method, I have no "self" argument to pass. Something like:
import inspect
class ToDo(object):
calls=[]
def do(self, **kwargs):
for call in self.calls:
if 'self' in call.args:
print "This will fail."
args = {}
for arg in call.args:
args[arg]=kwargs.get(arg, None)
call.call(**args)
TODO = ToDo()
class decorator(object):
def __call__(self, call):
if inspect.isfunction(call):
args = inspect.getargspec(call)[0]
elif inspect.isclass(call):
args = inspect.getargspec(call.__init__)[0][1:]
self.call = call
self.args = args
TODO.calls.append(self)
return call
TODO.do(a=1, b=2)
You can't really make that distinction. Here's an example:
>>> class A(object):
... pass
...
>>> def foo(x): return 3
...
>>> A.foo = foo
>>> type(foo)
<type 'function'>
>>> type(A.foo)
<type 'instancemethod'>
As you can see, your decorator could apply to foo, since it is a function. But you can then simply create a class attribute that references the function to create a decorated method.
(This example is from Python 2.7; I'm not sure if anything has changed in Python 3 to make the above behave differently.)
You cannot tell method from function but you can check if first argument looks like self:
def __call__(self, func):
def new_func(*args, **kw):
if len(args) and hasattr(args[0], '__dict__') \
and '__class__' in dir(args[0]) and func.__name__ in dir(args[0])\
and '__func__' in dir(getattr(args[0], func.__name__))\
and getattr(args[0], func.__name__).__func__ == self.func:
return my_func(*args[1:], **kw)
else:
return my_func(*args, **kw)
self.func = new_func
return new_func
But that won't work for nested decorators - next decorator will change the function and comparison with self.func won't work.
Another approach - to check if first argument's name of decorated function is self - this is
very strong convention in Python so may be good enough:
def __call__(self, func):
def new_func(*args, **kw):
if len(inspect.getfullargspec(func).args)\
and inspect.getfullargspec(func).args[0] == 'self':
return my_func(*args[1:], **kw)
else:
return my_func(*args, **kw)
self.func = new_func
return new_func