
Mike Graham wrote:
A common stumbling block for new users is writing decorators that take arguments. To create a decorator like
I question that assertion. Have you spent much time on the tutor@python.org mailing list? I have, and I can say that decorators is not something that many new users there are concerned about *at all*, let alone decorators which take arguments. I believe that decorator factories, and closures in general, are a moderately advanced technique, and not a stumbling block for most new users. I can't say I remember the last time I've seen anyone ask for help writing decorator factories on either the tutor or python-list mailing list. So I don't believe that making new syntax is neither needed or desirable. -1 on the idea. Some more issues/problems with the idea:
We write code like
def timesn(n) def decorator(f): def inner(y): return n * f(y) return inner return decorator
In my experience, most decorator factories (or factory-factories in general, not just for generators but any time you need to wrap a factory function in a closure) require at least one pre-processing step, and occasionally one or two post-processing steps as well: def timesn(n) pre_process() def decorator(f): pre_process() @functools.wraps(f) def inner(y): return n * f(y) post_process() return inner post_process() return decorator With your suggested syntax, you lose a scope, and it is unclear to me what this would do: def timesn(n)(f): pre_process() @functools.wraps(f) def inner(f): return n * f(y) post_process() return inner
which confuses many users and can be a handful to type. I wonder if it would be clearer for people to write
def timesn(n)(f): def inner(y): return n * f(y) return inner
which is more concise and looks a lot more like a non-parametric decorator someone might have written already. The syntax is mostly self-explaining and could potentially be useful in other contexts.
Concise, yes, but I disagree that it is self-explaining. I'm having a lot of difficulty in thinking of how I would explain it to even a moderately experienced user except by expanding it out to the explicit nested function form. I'm not sure I can even explain it to myself. What will happen when the user invariably writes something like this? def ordinary(a)(b): return a + b My guess is that they will get a syntax error, but it will be a syntax error in the wrong place: instead of clearly flagging the error (a)(b) as invalid syntax, as you get now py> def ordinary(a)(b): File "<stdin>", line 1 def ordinary(a)(b): ^ SyntaxError: invalid syntax Instead, the user will get a less useful syntax error on the following line, where the compiler sees that the function def is not followed immediately by another function def. py> def ordinary(a)(b): ... return a + b File "<stdin>", line 1 return a + b ^ SyntaxError: invalid syntax So whatever clarity you (allegedly) gain when writing decorator factories, you lose when making an error.
There exist tools like the decorator library to try to simplify this already, but in my experience they mostly serve to confuse people using decorators for the first time more.
In my opinion, that's because the decorator and decorator factory concept are already as simple as they can possibly be. Anything you do to them adds complexity, not reduces it. I mean, it's a simple concept: a decorator is a kind of function which returns a function. If you put that inside a third function, you have a function which returns a function which returns a function. Any syntax to disguise that simplicity is only going to make things more complex, not less. I haven't given any thought to how this will effect class decorators, but I suspect it will run into the same problems. -- Steven