Meta decorator with parameters, defined in explicit functions

Steven D'Aprano steve+comp.lang.python at pearwood.info
Tue Jun 28 04:39:56 EDT 2016


On Tuesday 28 June 2016 15:02, Ben Finney wrote:

> Howdy all,
> 
> I want an explicit replacement for a common decorator idiom.
> 
> There is a clever one-line decorator that has been copy-pasted without
> explanation in many code bases for many years::
> 
>     decorator_with_args = lambda decorator: lambda *args, **kwargs: lambda
>     func: decorator(func, *args, **kwargs)

I've never seen it before, and I'll admit I really had to twist my brain to 
understand it, but I came up with an example showing the traditional style of 
decorator-factory versus this meta-decorator.

# Standard idiom.

def chatty(name, age, verbose=True):  # the factory
    def decorator(func):  # the decorator returned by the factory
        if verbose:
            print("decorating function...")
        @functools.wraps(func)
        def inner(*args, **kwargs):
            print("Hi, my name is %s and I am %d years old!" % (name, age))
            return func(*args, **kwargs)
        return inner
    return decorator

@chatty("Bob", 99)
def calculate(x, y, z=1):
    return x+y-z


# Meta-decorator variant.

decorator_with_args = (lambda decorator: lambda *args, **kwargs: lambda func: 
decorator(func, *args, **kwargs))

@decorator_with_args
def chatty(func, name, age, verbose=True):
    if verbose:
        print("decorating function...")
    @functools.wraps(func)
    def inner(*args, **kwargs):
        print("Hi, my name is %s and I am %d years old!" % (name, age))
        return func(*args, **kwargs)
    return inner

@chatty("Bob", 99)
def calculate(x, y, z=1):
    return x+y-1



I agree -- it's very clever, and completely opaque in how it works. The easy 
part is expanding the lambdas:

def decorator_with_args(decorator):
    def inner(*args, **kwargs):
        def innermost(func):
            return decorator(func, *args, **kwargs)
        return innermost
    return inner


but I'm not closer to having good names for inner and innermost than you.


-- 
Steve




More information about the Python-list mailing list