decorator 

Send to Kindle
home » snippets » python » decorator



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)

Ref: functools.update_wrapper

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