[Python-Dev] The decorator module
Michele Simionato
michele.simionato at gmail.com
Fri May 6 17:05:07 CEST 2005
On 5/6/05, Guido van Rossum <gvanrossum at gmail.com> wrote:
> [Michele]
> > Honestly, I don't care, since "eval" happens only once at decoration time.
> > There is no "eval" overhead at calling time, so I do not expect to have
> > problems. I am waiting for volunteers to perform profiling and
> > performance analysis ;)
>
> Watch out. I didn't see the code referred to, but realize that eval is
> *very* expensive on some other implementations of Python (Jython and
> IronPython). Eval should only be used if there is actual user-provided
> input that you don't know yet when your module is compiled; not to get
> around some limitation in the language there are usually ways around
> that, and occasionally we add one, e.g. getattr()).
I actually posted the code on c.l.p. one month ago asking if there was
a way to avoid "eval", but I had no answer. So, let me repost the code
here and see if somebody comes out with a good solution.
It is only ~30 lines long (+ ~30 of comments & docstrings)
## I suggest you uncomment the 'print lambda_src' statement in _decorate
## to understand what is going on.
import inspect
def _signature_gen(func, rm_defaults=False):
argnames, varargs, varkwargs, defaults = inspect.getargspec(func)
argdefs = defaults or ()
n_args = func.func_code.co_argcount
n_default_args = len(argdefs)
n_non_default_args = n_args - n_default_args
non_default_names = argnames[:n_non_default_args]
default_names = argnames[n_non_default_args:]
for name in non_default_names:
yield "%s" % name
for i, name in enumerate(default_names):
if rm_defaults:
yield name
else:
yield "%s = arg[%s]" % (name, i)
if varargs:
yield "*%s" % varargs
if varkwargs:
yield "**%s" % varkwargs
def _decorate(func, caller):
signature = ", ".join(_signature_gen(func))
variables = ", ".join(_signature_gen(func, rm_defaults=True))
lambda_src = "lambda %s: call(func, %s)" % (signature, variables)
# print lambda_src # for debugging
evaldict = dict(func=func, call=caller, arg=func.func_defaults or ())
dec_func = eval(lambda_src, evaldict)
dec_func.__name__ = func.__name__
dec_func.__doc__ = func.__doc__
dec_func.__dict__ = func.__dict__ # copy if you want to avoid sharing
return dec_func
class decorator(object):
"""General purpose decorator factory: takes a caller function as
input and returns a decorator. A caller function is any function like this:
def caller(func, *args, **kw):
# do something
return func(*args, **kw)
Here is an example of usage:
>>> @decorator
... def chatty(f, *args, **kw):
... print "Calling %r" % f.__name__
... return f(*args, **kw)
>>> @chatty
... def f(): pass
>>> f()
Calling 'f'
"""
def __init__(self, caller):
self.caller = caller
def __call__(self, func):
return _decorate(func, self.caller)
Michele Simionato
More information about the Python-Dev
mailing list