Alex Martelli wrote:
Paul L. Du Bois <[EMAIL PROTECTED]> wrote:
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: 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


Paul, you really SHOULD have posted this BEFORE I had to send in the
files for the 2nd ed's Coobook... this gets my vote for the most
delightful abuse of sys._getframe even (and I've seen quite a few;-).

So, I couldn't figure out why this worked until I started to write an email to ask. Once I understood it, I figured it wouldn't hurt to send my thoughts out anyway to (1) verify that I understand it right, and (2) help anyone else who was trying to figure this out.



As I understand it sys._getframe(2).f_locals should get the names local to the stack frame two above the current one. So, in the context of:
fn(... for ... in args)
sys._getframe(2).f_locals should be looking at the names local to the 'anonymous' function in the 'fn' function, because one stack frame up from the 'args' function is the generator's 'next' function, and two stack frames up is the 'anonymous' function. That means that:
sys._getframe(2).f_locals['args']
gets whatever object has been bound to 'args' in:
def anonymous(*args):
So then in:
foo = fn(a + b * c for (a,b,c) in args)
foo(3,4,5)
foo(4,5,6)
sys._getframe(2).f_locals['args'] will get (3, 4, 5) in the first foo call, (4, 5, 6) in the second foo call, etc.



So basically the way a call like foo(3, 4, 5) works is:
(1) foo(3, 4, 5) calls gen.next() where gen is the generator expression
(2) gen.next() calls args.next()
(3) args.next() returns the (3, 4, 5) argument tuple of foo by looking up the stack frames
(4) gen.next() binds (3, 4, 5) to the names a, b, c respectively
(5) gen.next() returns the value of "a + b * c" for these bindings
(6) foo(3, 4, 5) returns the same value (as gen.next() did)


Does that seem about right?

Steve

P.S.  That's so *evilly* cool!
--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to