From: Yingjie Lan on
--- On Sun, 4/25/10, Chris Rebert <clp2(a)rebertia.com> wrote:

> From: Chris Rebert <clp2(a)rebertia.com>
> Subject: Re: NameError: how to get the name?
> To: "Yingjie Lan" <lanyjie(a)yahoo.com>
> Cc: "python list" <python-list(a)python.org>
> Date: Sunday, April 25, 2010, 10:09 AM
> On Sat, Apr 24, 2010, Yingjie Lan
> <lanyjie(a)yahoo.com>
> wrote:
> > On Sun, 4/25/10, Chris Rebert <clp2(a)rebertia.com>
> wrote:
> >> On Sat, Apr 24, 2010, Yingjie Lan <lanyjie(a)yahoo.com>
> wrote:
> >> > On Sat, 4/24/10, Gary Herron <gherron(a)islandtraining.com>
> wrote:
> >> >> Yingjie Lan wrote:
> >> >> > On Sat, 4/24/10, Steven D'Aprano
> <steve@--cybersource.com.au>
> wrote:
> >> >> >> On Sat, 24 Apr 2010, Yingjie Lan
> wrote:
> >> >> >>> I wanted to do something
> like this:
> >> >> >>>
> >> >> >>> while True:
> >> >> >>>   try:
> >> >> >>>     def fun(a, b=b, c=c):
> >> >> pass
> >> >> >>>   except NameError as ne:
> >> >> >>>     name =
> >> >> get_the_var_name(ne)
> >> >> >>>     locals()[name] = ''
> >> >> >>>   else: break
> >> >> >>>
> >> >> >> This won't work. Writing to
> locals() does
> >> not
> >> >> actually
> >> >> >> change the local variables. Try
> it inside
> >> a
> >> >> function, and you will see it
> >> >> >> doesn't work:
> >> >
> >> > No it DOESN'T work, and both of you are
> precisely
> >> correct.
> >> > Just for playing around, I substituted
> >> > "locals()" by "globals()" and it worked as
> desired:
> >> <snip>
> >> > Thanks for the information! BTW, why would
> >> > locals() and globals() differ in this
> respect?
> >>
> >> The module-level (i.e. global) namespace is
> implemented by
> >> CPython
> >> using an actual dictionary; globals() returns a
> proxy to
> >> that
> >> dictionary and lets you manipulate it.
> >> In contrast, as an optimization, CPython
> implements local
> >> variables in
> >> functions using predetermined offsets into an
> array of
> >> predetermined
> >> length; the dictionary returned by locals() is
> dynamically
> >> constructed
> >> on-demand and does not affect the actual array
> used for the
> >> local
> >> variables (I suppose it could have been made to do
> so, but
> >> there's
> >> probably a complexity or optimization reason for
> why not).
> >>
> >
> > Thanks, that's good to know. The locals() behaves
> rather
> > strangely, as can be demonstrated by the following
> two
> > tests (the first one is from Steven, thanks Steve):
> >
> > #file: fun.py:
> >
> > def test():
> >    x = 1
> >    print (x)
> >    locals()['x'] = 2
> >    print (x, locals()['x'])
> >
> > def test2():
> >    locals()['x'] = 2
> >    print (locals()['x'])
> >    print x
> >
> > test()
> > test2()
> >
> > -----And the output: python fun.py-------
> > 1
> > (1, 1)
> > 2
> > Traceback (most recent call last):
> >  File "fun.py", line 21, in <module>
> >    test2()
> >  File "fun.py", line 17, in test2
> >    print x
> > NameError: global name 'x' is not defined
> >
> > -------------
> >
> > I don't know how to make sense out of it.
> > Any suggestions?
>
> My working theory is that attempting to modify a key in
> locals()
> corresponding to an extant variable has no effect, even on
> just the
> locals() dictionary itself, and is ignored, but adding or
> modifying
> other keys in locals() works like a normal dictionary.
>
> def test3():
>     x = 1
>     print x
>     locals()['x'] = 2
>     locals()['y'] = 3
>     print x, locals()['x'], locals()['y']
>     print y
>
> $ python test3.py
> 1
> 1 1 3
> Traceback (most recent call last):
>   File "Desktop/tmp.py", line 22, in <module>
>     test3()
>   File "Desktop/tmp.py", line 7, in test3
>     print y
> NameError: global name 'y' is not defined
>
>
> As for why you get a NameError in test2() [assuming that's
> part of
> what's confusing you], there's no assignment to `x` in
> test2(), so
> Python reasons you must be referring to a global variable
> `x`. But
> there is no such global variable, hence the NameError.
>
> Cheers,
> Chris
> --

Thanks, very nice observations. What's puzzling me is this:
in the first test, it seems locals() ignores assignment,
thus locals()['x'] == x holds True, but in the second one,
as 'x' does not exists, it allows assignment. I might assume
this: locals() keeps two mappings: one is the true locals,
another is an ordinary dict (let's called the faked one).
when you look up a value, it first looks into the true locals,
then the faked one only if not found there. As for setting a
value, it always works on the faked one.

That's my best guess, anyway.

It seems better to be explicit: simply throws an exception
whenever one tries to set a value for locals()['x'] -- just
say that locals() are unlike globals(), it is read only.


Yingjie