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