Replacement for lambda that uses language features new to Python 2.4. One implementation has a neat (ab)use of sys._getframe(); the other is portable.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | # Implementation 1: not portable
import sys
def fn(gen):
"""Turns a generator expression into a callable."""
def anonymous(*args):
return gen.next()
return anonymous
def args():
"""Works with fn(); yields args passed to anonymous()."""
while True:
# Stack frame will look like:
# anonymous(args=(...)) "foo(3,4,5)"
# gen.next() "return gen.next()"
# args.next() internal genexp code
yield sys._getframe(2).f_locals['args']
args = args()
foo = fn(a + b * c for (a,b,c) in args)
assert foo(3,4,5) == 3+4*5
assert foo(4,5,6) == 4+5*6
# Implementation 2: not thread-safe (but it could be)
class SingleElementIterator:
"""Iterator that must be constantly fed by assigning to .value"""
def __iter__(self):
return self
def next(self):
value = self.value
del self.value
return value
args2 = SingleElementIterator()
def fn2(gen):
"""Turns a generator expression into a callable."""
def anonymous(*args_):
args2.value = args_
return gen.next()
return anonymous
foo = fn2(a + b * c for (a,b,c) in args2)
assert foo(3,4,5) == 3+4*5
assert foo(4,5,6) == 4+5*6
|
Guido has expressed the desire to remove "lambda" for Python 3000. There was a discussion on comp.lang.python about what new language feature (if any) should replace lambda. Some of the proposed syntaxes led me to try a pure-python implementation.
The basic idea is to use a generator expression to implement an "expression object". The function "fn" (the name is taken from the Arc language) wraps the generator in a callable and returns it. The wrapper is responsible for feeding its arguments to the genexp; this is done by cooperating with the genexp's input iterator.
In the first (CPython-specific) implementation, the cooperation is simple: the wrapper just needs to name its parameter "args". The iterator reaches up the stack to extract the local variable.
In the second implementation, the wrapper stores its args in what amounts to a global variable, and the input iterator "args2" almost immediately consumes it. There is no way for an anonymous function to execute between the time args.value is set and the time it's consumed; thus the use of global state is safe.
However, the fact that the implementation is stateful at all makes it unsafe in the presence of threads. To make it safe, one would have to make SingleElementIterator put "value" in thread-local storage. This is left as an exercise for the reader :-).
This is never explicitly an issue for these implementations, but it's worth keeping in mind that a genexp's outermost iterator is evaluated immediately.
References: * Pep 3000 http://python.org/peps/pep-3000.html * comp.lang.python thread http://groups-beta.google.com/group/comp.lang.python/msg/41713ae1c0d7385a
using namespace for args. This doesn't try to solve the threading problem, but I think I prefer a solution that keeps 'args' in the 'fn' namespace, like:
which would then be used like:
That way 'args', which is intimately joined with 'fn', appears as such namespace-wise.
Perhaps a little prettier:
Also has the benefit of naming "functions" generated by fn as "fn":
Good points. I originally used a class for the "portable" version but got stuck because I didn't remember __new__. I'll update the recipe when I get a few tuits; thanks!