Just soliciting opinion on whether the following is reasonable or if there is a better approach. Basically I want a decorator that will apply to a function or a class that implements __call__.
You could just have a regular decorator and decorate the __call__ explicitly but then the decorator is tucked inside the class definition and is less obvious. Maybe I am missing a more straightforward solution.
import types
from functools import wraps
class dec:
""" Decorates either a class that implements __call__
or a function directly.
"""
def __init__(self, foo):
self._foo = foo
def __call__(self, target):
wraps_class = isinstance(target, types.ClassType)
if wraps_class:
fun = target.__call__
else:
fun = target
@wraps(fun)
def bar(*args, **kwds):
val = args[1] if wraps_class else args[0]
print self._foo, val
return fun(*args, **kwds)
if wraps_class:
target.__call__ = bar
return target
else:
return bar
@dec('A')
class a:
# you could decorate here, but it seems a bit hidden
def __call__(self, val):
print "passed to a:", val
@dec('B')
def b(val):
print "passed to b:", val
a()(11)
b(22)
In Python, decorators can either be functions or classes. In both cases, decorating adds functionality to existing functions. When we decorate a function with a class, that function becomes an instance of the class.
To decorate a function with a class, we must use the @syntax followed by our class name above the function definition. Following convention, we will use camel-case for our class name. In the class definition, we define two methods: the init constructor and the magic (or dunder) call method.
In Python, the @classmethod decorator is used to declare a method in the class as a class method that can be called using ClassName. MethodName() . The class method can also be called using an object of the class. The @classmethod is an alternative of the classmethod() function.
A decorator is a design pattern in Python that allows a user to add new functionality to an existing object without modifying its structure. Decorators are usually called before the definition of a function you want to decorate.
Personally, I would split this into two decorators: one that always wraps a function:
def func_dec(foo, is_method=False):
def wrapper(fun):
@wraps(fun)
def bar(*args, **kwds):
val = args[1] if is_method else args[0]
print foo, val
return fun(*args, **kwds)
return bar
return wrapper
And another that detects if it should modify a __call__
method or simply wrap a function:
def dec(foo):
def wrapper(obj):
if inspect.isclass(obj):
obj.__call__ = func_dec(foo, is_method=True)(obj.__call__)
return obj
else:
return func_dec(foo)(obj)
return wrapper
Note that inspect.isclass
will behave correctly with both old-style and new-style classes.
I don't really like your approach. The __call__()
method is used if an instance is called. Calling the class itself invokes __init__()
instead, so I don't see this to be really analogous.
Your decorator won't work for new-style classes (directly or indirectly derived from object
). Do yourself a favour and simply decorate __call__()
if this is what you want. Or write a factory function that creates and decorates instances of the class -- this would be in total analogy to decorating a function, because the instance is directly callable, and you don't have to mess arounf with the self
parameter.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With