[Python-ideas] Syntax for defining parametric decorators
Steven D'Aprano
steve at pearwood.info
Mon Jul 9 02:52:15 CEST 2012
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 at 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
More information about the Python-ideas
mailing list