views:

266

answers:

5

Hi there, I'm having a bit of trouble understanding what's going wrong with the following function:

def ness():
 pie='yum'
 vars()[pie]=4
 print vars()[pie]
 print yum

So When I run that I get this result:

>>> ness()
4
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in ness
NameError: global name 'yum' is not defined

If I don't write it as a function and just type it in on the command line one line at a time it works fine, like so:

>>> pie='yum'
>>> vars()[pie]=4
>>> print vars()[pie]
4
>>> print yum
4
>>> 

Edit: Suppose I wanted to make things a bit more complicated than this and instead of setting yum to a value and printing that value, I define some functions, and want to call one of them based on some input:

def ness(choo):
    dic={}
    dessert=()
    dnum=[10,100]
    desserts='pie'
    dic[dessert]=str(desserts[bisect(dnum,choo)])
    vars()[dic[dessert]]()
def p():
    print 'ummmm ummm'
def i():
    print 'hooo aaaaa'
def e():
    print 'woooo'

So when I call ness I get a key error:

>>> ness(3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in ness
KeyError: 'p'

Now I know I can do things like this with some elif statements, but I'm wondering if this would work too, and if using bisect like this would be more efficient (say if i need to check 1000 values of choo) than using elifs.

Thanks much for the assistance.

+1  A: 

It's not safe to modify the dict returned by vars()

vars([object])¶

Without an argument, act like locals().

With a module, class or class instance object as argument (or anything else that has a dict attribute), return that attribute.

Note

The returned dictionary should not be modified: the effects on the corresponding symbol table are undefined.

Your second example is a special case. vars() is equivalent to globals() in the global namespace, and the dict returned by globals() behaves as you would expect ( but is frowned upon )

>>> id(vars()),id(globals())
(3085426868L, 3085426868L)
gnibbler
+4  A: 

vars() within a function gives you the local namespace, just like locals() -- see the docs. Outside of a function (e.g. at the prompt) locals() (and vars() of course) gives you the module's global namespace, just like globals(). As the docs say, trying to assign to a function's local variable through locals() (or equivalently, vars() inside a function) is not supported in Python. If you want to assign to a global variable, as you do when you're at the prompt (or otherwise outside of a function), use globals() instead of vars() (maybe not the cleanest approach -- global variables are understandably frowned upon -- but it does work).

Alex Martelli
A: 

vars() is equivalent to locals(), which in the case of the function is the local variables in its scope and at in the interactive interpreter at the scope you have it, vars() is globals(). locals() is for reading only; the effects of trying to change it are undefined (and in practice, just doesn't work). globals() can be modified, but you still should never directly put anything in the dict it returns.

Mike Graham
A: 

[Edit: I must be wrong here, since the 'exec' example works.]

As everyone points out, it's a bad idea to modify vars(). You can understand the error, though, by realizing that python in some sense doesn't "see" that "yum" is a local. "print yum" is still resolved as a global reference; this happens before any code is executed.

It's the same reason you get an UnboundLocalError from:

>>> y = 100
>>> def foo(x):
...   if x == 1:
...     y = 10
...   print y
... 
>>> foo(1)
10
>>> foo(2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in foo
UnboundLocalError: local variable 'y' referenced before assignment
Johann Hibschman
+2  A: 

There is way to do it with exec

>>> def ness():
...  pie='yum'
...  exec pie+"=4"
...  print vars()[pie]
...  print yum
...
>>>
>>> ness()
4
4

But Instead of doing that, using a new dict is better and safe

>>> def ness():
...  dic={}
...  pie='yum'
...  dic[pie]=4
...  print dic[pie]
...  print dic['yum']
...
>>> ness()
4
4
>>>
S.Mark
Using exec did the trick for the more complicated example I wrote above (no key errors like trying to use dict along with vars. What exactly makes using dict safer than exec? Is it what gnibbler said below:"The returned dictionary should not be modified: the effects on the corresponding symbol table are undefined"Doe this mean using exec in this manner can cause unforeseeable issues?
Jamie
exec can execute any python codes, for eg, `exec "os.system('reboot_your_pc')", so its the best to avoid using it.
S.Mark
Also exec can give you a confusing stacktrace and will be much slower than an explicit dictionary (it has to be parsed every time from scratch).
viraptor