If you want X, you know where to find it (was Re: do...until wisdom needed...)

Alex Martelli aleaxit at yahoo.com
Thu Apr 19 04:15:16 EDT 2001


"Andrew Dalke" <dalke at acm.org> wrote in message
news:9bm47r$qo2$1 at slb5.atl.mindspring.net...
    [snip]
> >(Is there a way in Python of finding local
> >variables by name on the Python stack so that a procedure can get and
> >set variables in the caller's environment?  If so, you could do it,
> >but it would be pretty ugly.)
>
> Yes and no.  Python deliberately makes it hard.  You need
> to raise an exception and look up the stack frames.  There

In 2.1, sys._getframe (to "be used for internal and
specialized purposes only") provides a somewhat handier
alternative to the classic raise-and-look-up idiom.
But I don't think this has changed what you can
actually _do_ once you've got the caller's frame
object -- "see, but not touch", more or less.

Consider, for example:


import sys

if hasattr(sys, '_getframe'):
    def callerlocals():
        return sys._getframe(2).f_locals
else:
    def callerlocals():
        try: raise 'catch me'
        except: return sys.exc_traceback.tb_frame.f_back.f_back.f_locals

any = 'pass'
def f():
    x = 23
    print x
    g(x=24)
    exec any
    print x
    g(y=37)
    exec any
    print x,y


def g(**kws):
    print callerlocals()
    callerlocals().update(kws)
    print callerlocals()

f()


In both 2.0 and 2.1, this outputs:

23
{'x': 23}
{'x': 23}
23
{'x': 23}
{'x': 23, 'y': 37}
23 37


I.e., the "exec 'pass'" trick has allowed the addition
of a new binding for y, but not the rebinding of x
anyway.  If you comment out the exec statements, then
f's attempt to access y fails:

23
{'x': 23}
{'x': 23}
23
{'x': 23}
{'x': 23, 'y': 37}
23
Traceback (most recent call last):
  File "caller.py", line 28, in ?
    f()
  File "caller.py", line 20, in f
    print x,y
NameError: global name 'y' is not defined

The dictionary object has allowed the update (sort
of: it has allowed the addition of y->37, but not
the rebinding of x->23 to x->24), but the code is
not looking at that dictionary to fetch 'y' -- the
optimizer, absent an exec statement, having decided
that 'y' is a global, not a local, variable, it
has generated bytecode to fetch a global specifically.


Alex






More information about the Python-list mailing list