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)
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.
That's a pretty slick idea. It seems fine to me, although it might be more pythonic to decorate __call__
directly since "explicit is better than implicit". There's a little bit of conceptual overhead to having one decorator that does two things.
(I wonder if it would be worse or better to make a decorator that takes any function decorator and turns it into a dual function/class decorator...)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.