Python vars() global name error - python

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.

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).

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
>>>

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)

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.

[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

Related

How to make eval() use passed "locals"? [duplicate]

I want to evaluate a lambda expression using the built-in eval function, with a variable y defined in the 'locals' argument. Sadly the result function doesn't work:
>>>x = eval('lambda: print(y)',{},{'y':2})
>>>x()
Traceback (most recent call last):
File "<pyshell#75>", line 1, in <module>
x()
File "<string>", line 1, in <lambda>
NameError: name 'y' is not defined
But with y defined in the 'globals' argument, it does work:
>>> x = eval('lambda: print(y)', {'y': 2},{})
>>> x()
2
As I understand, the lambda expression should have captured the whole current frame including all the variables defined in the globals and locals arguments.
So why does Python behave like this?
Quite simply: passing a populated locals directory doesn't change the way python parses a function code and decides which names are locals and which are globals.
Local names are arguments names and names that are bound within the function's body and not explicitely declared globals or non-locals. Here, y is not an argument, and is not bound within the function's body (which is impossible in a lambda anyway) so it is marked by the compiler as global.
Now those global and local environment are those used to evaluate the expression itself (here the full 'lambda: print(y)' expression), not "the local environment for the lambda's body", so even if there was a way to make y local to the function's body (hint: there's one - but it won't solve your problem) this name would still not be "automagically" set to the the 'y' value in the locals dict passed to eval.
BUT this is actually not a problem - the function created by eval("lambda: y", {"y":42}) captures the globals dict passed to eval and uses it in place of the module/script/whatever globals, so it will work as expected:
Python 3.4.3 (default, Nov 28 2017, 16:41:13)
[GCC 4.8.4] on linux
>>> f = eval("lambda: y+2", {'y':2}, {})
>>> f()
4
>>> y = 42
>>> f()
4
Now you have the explanation, I whish to add that eval() is very dangerous and most often than not there's a much better solution, depending on the exact problem you're trying to solve. You didn't provide any context so it's impossible to tell more but I have to say I actually have a hard time thinking of a concrete use-case for such a thing as f = eval("lambda: whatever").
x = eval('lambda: print(y)',{},{'y':2}) is not equal to this line x = eval('lambda: print(y)', {'y': 2},{}) in first part change params order and it should work

Passing keywords as parameters

Is it against Python "style" to put keywords as function parameters?
e.g.
def findStartTime(dict):
for key, value in dict.iteritems():
#do something based on values in dictionary
return something
...and besides style,
are there any potential problems that will show up?
What if for some reason in the future you want to call dict() inside the function to create a dictionary? Anyway, give meaningful names to variables instead of naming them by their type.
The issue come down to scope. If you call something dict, you wont be able to access dict methods.
For example, if you called a variable math then you would be unable to access math.method():
>>> import math
>>> math = "MATH"
>>> rounded_number = math.floor(3.14)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'floor'
>>> import math
>>> rounded_number = math.floor(3.14)
>>> rounded_number
3.0
So I import math, but then override the module with a string. Hence the AttributeError
Then I reimport math and boom, math.floor() is a valid method again. So basically, if you call a dict "dict" it will prevent using the dict() constructor and other methods.
Hope this helps!

Intercepting global name lookup in a Python exec environment

I realize that this may be a fragile approach, but I'm looking for a way to intercept global name lookups (and also to provide a value/binding when the lookup fails) under 'exec'.
Use case: I want to provide a restricted execution environment for some external scripts written by users. I am trying to tailor the script conventions and namespace construction to very unsophisticated users, so I'd like them to be able to call a bunch of functions as if they were "global" without having to construct the entire dictionary by hand ahead of time.
Ergo, I'd like to intercept the global/module namespace lookup of SomeIdentifierNameTheyMayUse, and to dynamically bind that name to something computed rather than something already bound in the namespace.
Is something like this possible in general?
I managed to get something sort-of working, but it has problems, as you can see below:
class mydict( dict ):
def __missing__( self, key ):
print "__missing__:", key
return 99
d = mydict()
d[ '__builtins__' ] = {}
code = """
# triggers __missing__ call as desired, prints 99
print this_bad_sym_is_ok
def action1():
print 'action1!'
# does not trigger __missing__. Why? And how can I fix it?
print this_bad_sym_is_not
"""
exec code in d
print "d=", d
exec 'action1()' in d
which currently produces:
__missing__: this_bad_sym_is_ok
99
d= {'__builtins__': {}, 'action1': <function action1 at 0x107d6b2a8>}
action1!
Traceback (most recent call last):
File "t.py", line 25, in <module>
exec 'action1()' in d
File "<string>", line 1, in <module>
File "<string>", line 10, in action1
NameError: global name 'this_bad_sym_is_not' is not defined
Even if it's not possible to do something similar to this, I'd still like to understand why it's not working.
Thanks!
Maybe this helps: https://wiki.python.org/moin/SandboxedPython
It explains the restricted execution environment.
This is an implementation: https://pypi.python.org/pypi/pysandbox/

Using function names as variables in python

I had an interesting (potentially stupid) idea: What happens if I use a built-in function name as a variable to assign some object (say integer). Here's what I tried:
>>> a = [1,2,3,4]
>>> len(a)
4
>>> len = 1
>>> len(a)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: 'int' object is not callable
Seems like python does not treat function and variable names differently. Without restarting the python interpreter, is there a way to assign len back to the function? Or undo the assignment len = 1?
Use del len:
>>> a=[1,2,3,4]
>>> len=15
>>> len(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
>>> del len
>>> len(a)
4
From docs.python.org:
Deletion of a name removes the binding of that name from the local or global namespace, depending on whether the name occurs in a global statement in the same code block. If the name is unbound, a NameError exception will be raised
Technically you can get it back from __builtin__
from __builtin__ import len
But please don't name stuff len, it makes sensible programmers angry.
Okay, for a start don't name your variable after the builtins, secondly if you want to respect other functions then respect namespaces for example
import time
time.asctime()
asctime = 4253
time.asctime() # Notice that asctime here is unaffected as its inside the time module(s) namespace

Exec statement won't properly define names [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
declaring a global dynamic variable in python
>>> def f():
global cat
exec 'cat'+'="meow"'
return
>>> f()
>>> cat
Traceback (most recent call last):
File "<pyshell#23>", line 1, in <module>
cat
NameError: name 'cat' is not defined
This is just a stripped down example of the issue I've come across. In my actual script, I need various instances of a class be created and named, hence the exec statement.
Just calling
exec 'cat'+'="meow"'
directly in the shell works fine, but as soon as it's packed in a function, it doesn't seem to work anymore.
I still don't understand why you are using exec, its a bad design choice and alternatives are usually easier, for example instead of global and then something else you could simply do this
ns = {}
def f():
ns["cat"] = "miow"
print ns
Now isn't that cleaner?
looks like the exec ignores the global, the documentation is a bit vague. but this works:
>>> def f():
... global cat
... exec 'global cat; cat'+'="meow"'
...
>>>
>>> f()
>>> cat
'meow'

Categories