[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