Links
The PyPI Decorator module
My lazy_refs code
@memoized_property def decorator(self): try: import decorator return decorator except ImportError: from ck_3p import decorator return decorator
Decorator is a decorator – (alternate link)
@ck.refs.decorator.decorator def trace_call(func, *args, **kwargs): # Do whatever. return func(*args, **kwargs)
Traditionally
Suppose you were writing a memoize decorator.
def memoize_uw(func): func.cache = {} def memoize(*args, **kw): # Lookup in cache or call func(*args, **kw) and # return that return functools.update_wrapper(memoize, func)
which is then used like this
@memoize_uw def f1(x): time.sleep(1) # simulate some long computation return x
This has the following disadvantages: - It isn't signature preserving – the returned function accepts varargs. - You already have one level of nesting. If you had to pass params to memoize_uw, then you need an additional level of nesting.
First step - fix the signature preserving part and remove the nesting
# The nested func goes outside. It also accepts the # function as its first param. def _memoize(func, *args, **kw): # You could have stored additional stuff on that # decorator func that you can access here. # Do work and return result pass # Now you define the decorator function. def memoize(func): # Adding an attrib to the func func.cache = {} return decorator(_memoize, func)
And use it to decorate something.
@memoize def heavy_computation(): time.sleep(2) return "done"
This would preserve the signature.
>>> print getargspec(heavy_computation) ArgSpec(args=[], varargs=None, keywords=None, defaults=None)
decorator is a decorator
Decorator is a decorator – (alternate link)
So calling decorator
with two args, like above,
decorator
(_memoize
, func
) returns func
wrapped such that
calling the wrapped func
calls _memoize
passing it the func
and the other args.
However, if decorator
is passed just one arg, it acts as a
decorator
creating decorator
.
So you could write the memoize function this way.
@decorator def _memoize(func, *args, **kw): # Of course, you couldn't have stored # anything on func this time. pass
Writing decorators that are configured with params.
e.g.
def blocking(not_avail): @decorator def blocking(f, *args, **kw): # The not_avail param is in the closure, so you can # access it here. print "I was decorated with", not_avail # Do computation and return result. pass return blocking @blocking("Please wait ...") def read_data(): pass
Converting old style decorators to new style
If you have an existing decorator, say old_decorator
,
which you would have used this way
@old_decorator def foo(...): pass
You can instead do this:
def new_decorator(func) return decorator_apply(old_decorator, func) @new_decorator def foo(...): pass