[Python-ideas] Allow lambda decorators

Carl Johnson carl at carlsensei.com
Mon Feb 9 01:41:37 CET 2009


A few months back there was a discussion of how code like this gives 
"surprising" results because of the scoping rules:
 >>> def func_maker():
... fs = []
... for i in range(10):
... def f():
... return i
... fs.append(f)
... return fs
...
 >>> [f() for f in func_maker()]
[9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
Various syntax changes were proposed to get around this, but nothing 
ever came of it.
Also recently, I tried to propose a new syntax to allow Ruby-like 
blocks in Python without sacrificing Python's indenting rules. My idea 
was that "@" would mean "placeholder for a function to be defined on 
the next line" like so:
 >>> sorted(range(10), key=@):
... def @(item):
... return -item
...
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
Thinking about it some more, I've realized that the change I proposed 
is unnecessary, since we already have the decorator syntax. So, for 
example, the original function can be made to act with the "expected" 
scoping by using an each_in function defined as follows:
 >>> def each_in(seq):
... return lambda f: [f(item) for item in seq]
...
 >>> def func_maker():
... @each_in(range(10))
... def fs(i):
... def f():
... return i
... return f
... return fs #Warning, fs is a list, not a function!
...
 >>> [f() for f in func_maker()]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
On the one hand, I can imagine some people thinking this is decorator 
abuse, since the each_in decorator produces a list and not a function. 
If so, I suppose the lambda might be changed to
 >>> def each_in(seq):
... return lambda f: lambda: [f(item) for item in seq]
...
 >>> def func_maker():
... @each_in(range(10))
... def fs(i):
... def f():
... return i
... return f
... return fs() #Warning, fs is a function, not a list
...
 >>> [f() for f in func_maker()]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In either version the important thing is that it provides a scoped 
version of a for-loop, which those from a C++ background might be more 
conditioned to expect. Possibly, such a function could be added to the 
functools or the itertools. It would be useful for when scoping issues 
arise, for example when adding a bunch of properties or attributes to 
a class.
Thinking about it some more though, it's hard to see why such a 
trivial function is needed for the library. There's no reason it 
couldn't just be done as an inline lambda instead:
 >>> def func_maker():
... @lambda f: [f(i) for i in range(10)]
... def fs(i):
... def f():
... return i
... return f
... return fs #Warning, fs is a list, not a function!
...
 >>> [f() for f in func_maker()]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
OK, actually there is one reason this couldn't be done as a lambda:
 >>> @lambda f: [f(i) for i in range(10)]
 File "<stdin>", line 1
 @lambda f: [f(i) for i in range(10)]
 ^
SyntaxError: invalid syntax
This is because the decorator grammar asks for a name, not an 
expression, as one (well, OK, me a couple months ago) might naively 
expect.
decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
decorators: decorator+
decorated: decorators (classdef | funcdef)
and not
decorator: '@' test NEWLINE
decorators: decorator+
decorated: decorators (classdef | funcdef)
Changing the grammar would also allow for the rewriting of the sorted 
example given earlier:
 >>> @lambda key: sorted(range(10), key=key)
... def sorted_list(item):
... return -item
...
 >>> sorted_list
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
Of course, the use of lambda decorator is strictly speaking unnecessary,
 >>> k = lambda key: sorted(range(10), key=key)
 >>> @k
... def sorted_list(item):
... return -item
...
 >>> del k
 >>> sorted_list
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
But, I think I think that allowing lambda decorators would be 
convenient for a number of situations and it would give a simple 
answer to those asking for a multiline lambda or Ruby-like blocks.
What do other people think?
-- Carl


More information about the Python-ideas mailing list

AltStyle によって変換されたページ (->オリジナル) /