On Thu, 30 Jun 2016 12:43 pm, Lawrence D’Oliveiro wrote: > On Tuesday, June 28, 2016 at 5:03:08 PM UTC+12, Ben Finney wrote: >> 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) >> >> My problem with this is precisely that it is clever: it explains nothing >> about what it does, has many moving parts that are not named, it is >> non-obvious and lacks expressiveness. > > It is written in a somewhat roundabout way: why not just > > decorator_with_args = lambda decorator, *args, **kwargs : lambda func > : decorator(func, *args, **kwargs) > > ? Could it be this was not valid in earlier versions of Python?
Your version has a much inferior API than the original. You can't wrap your decorators ahead of time, you have to keep calling decorator_with_args each time you want to use them. Contrast your version: # LDO's version import functools decorator_with_args = ( lambda decorator, *args, **kwargs : lambda func : decorator(func, *args, **kwargs) ) 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 @decorator_with_args(chatty, "Bob", 99) def calculate(x, y, z=1): return x+y-z @decorator_with_args(chatty, "Sue", 35, False) def spam(n): return ' '.join(['spam']*n) Here's the original. It's a much better API, as the decorator "chatty" only needs to be wrapped once, rather than repeatedly. For a library, it means that now you can expose "chatty" as a public function, and keep decorator_with_args as an internal detail, instead of needing to make them both public: # original meta decorator version import functools 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-z @chatty("Sue", 35, False) def spam(n): return ' '.join(['spam']*n) Think of the use-case where you are the author of a library that provides various decorators. `decorator_with_args` is an implementation detail of your library: *you* say: @decorator_with_args def chatty(func, name, age, verbose=True): ... @decorator_with_args def logged(func, where_to_log): ... @decorator_with_args def memoise(func, cache_size): ... and then offer chatty, logged and memoise as public functions to the users of your library, who just write: @memoise(1000) def myfunc(arg): ... etc. as needed. But with your version, you have to make decorator_with_args a public part of the API, and require the user to write: @decorator_with_args(memoise, 1000) def myfunc(arg): ... which I maintain is a much inferior API for the users of your library. > I don’t know why this fear and suspicion of lambdas is so widespread among > Python users ... former Java/C# programmers, perhaps? Not so much fear as a little healthy respect for them, I think. -- Steven “Cheer up,” they said, “things could be worse.” So I cheered up, and sure enough, things got worse. -- https://mail.python.org/mailman/listinfo/python-list