weird behaviour of late import and scopes - python

I have just discovered this strange scoping behaviour of both Python 2 and 3. When I'm adding a late import for a sub-module, the main import of toplevel module stops working. Viable example:
import os
def start():
import sys
print('in modules?', 'os' in sys.modules)
print('in globals?', 'os' in globals())
print('in locals?', 'os' in locals())
print('os =', os)
import os.path
os.path.exists('useless statement')
start()
The output will be:
in modules? True
in globals? True
in locals? False
Traceback (most recent call last):
File "test.py", line 15, in <module>
start()
File "test.py", line 9, in start
print('os =', os)
UnboundLocalError: local variable 'os' referenced before assignment
Any ideas?

This is nothing special about import statements. It's just how the scoping works in Python. If you're assigning a value to a label, it is local to the scope unless explicitly defined global.
Try this code -
a = 2
def start():
print a
a = 3
start()
This also fails with UnboundLocalError as your code because statement a = 3 makes the label a local to function start.

Related

Time find of builtin function sum [duplicate]

I'm trying to find out how much time it takes to execute a Python statement, so I looked online and found that the standard library provides a module called timeit that purports to do exactly that:
import timeit
def foo():
# ... contains code I want to time ...
def dotime():
t = timeit.Timer("foo()")
time = t.timeit(1)
print "took %fs\n" % (time,)
dotime()
However, this produces an error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in dotime
File "/usr/local/lib/python2.6/timeit.py", line 193, in timeit
timing = self.inner(it, self.timer)
File "<timeit-src>", line 6, in inner
NameError: global name 'foo' is not defined
I'm still new to Python and I don't fully understand all the scoping issues it has, but I don't know why this snippet doesn't work. Any thoughts?
Change this line:
t = timeit.Timer("foo()")
To this:
t = timeit.Timer("foo()", "from __main__ import foo")
Check out the link you provided at the very bottom.
To give the timeit module access to functions you define, you can pass a setup parameter which contains an import statement:
I just tested it on my machine and it worked with the changes.
With Python 3, you can use globals=globals()
t = timeit.Timer("foo()", globals=globals())
From the documentation:
Another option is to pass globals() to the globals parameter, which
will cause the code to be executed within your current global
namespace. This can be more convenient than individually specifying
imports
You can try this hack:
import timeit
def foo():
print 'bar'
def dotime():
t = timeit.Timer("foo()")
time = t.timeit(1)
print "took %fs\n" % (time,)
import __builtin__
__builtin__.__dict__.update(locals())
dotime()
t = timeit.Timer("foo()", "from __main__ import foo")
Since timeit doesn't have your stuff in scope.
add into your setup "import thisfile; "
then when you call the setup function myfunc() use "thisfile.myfunc()"
eg "thisfile.py"
def myfunc():
return 5
def testable(par):
pass
t=timeit.timeit(stmt="testable(v)",setup="import thisfile; v=thisfile.myfunc();").repeat(10)
print( t )

Python3-Issue with calling exec(open().read()) inside a function

I'm having an issue with running a python script in a python script that i simply do not understand:
Assume we have 2 files in the same directory: 'init.py' and 'text.py'
init.py:
X = 5
print("init.py was run")
test.py:
exec(open("./init.py").read())
print("X = %s" %X)
If I run test.py now, I get
init.py was run
X = 5
However, if I change test.py into:
def func_call( filename):
exec(open(filename).read())
print("X = %s" %X)
func_call("./init.py")
I get:
init.py was run
Traceback (most recent call last):
File "test.py", line 5, in
func_call("./init.py")
File "test.py", line 3, in func_call
print("X = %s" %X)
NameError: name 'X' is not defined
Can someone explain to me why this leads to different results?
Is there a workaround for this?
My goal is to initializes most of my variables by running a python script and accessing the variables set up in that python script.
According to exec_documentation:
If exec gets two separate objects as globals and locals, the code will be executed as if it were embedded in a class definition.
Inside method globals() and locals() are different objects:
def method():
print(globals() == locals())
exec('X=10')
print('Method execution =', X)
method()
output:
False
NameError: name 'X' is not defined
In global level this objects are equal:
print(globals() == locals())
exec('X=99')
print('Global exec =', X)
Output:
True
Global exec = 99
So If you want to do it via method, you need to pass the same object to exec. For your code it would look like this:
def func_call(filename):
exec(open(filename).read(), globals(), globals())
print("X = %s" %X)
func_call("./init.py")
Nevertheless, as I mentioned in comment, create file with consts and import it. Try to avoid using exec/eval at all costs, unless you are 100% sure what you are doing.

Confused why import os; os.environ['a'] = 'x'; import os.path raises UnboundLocalError

Can someone explain why the following happens? I had a look at Should I use `import os.path` or `import os`? which is informative, vaguely similar, but didn't really clarify it for me.
If I comment out import os.path or add it directly after import os there's no error.
$ python -V
Python 2.7.2
$ cat min.py
import os
def main():
os.environ['blah'] = 'bloo'
import os.path
if __name__ == '__main__':
main()
$ python min.py
Traceback (most recent call last):
File "min.py", line 9, in <module>
main()
File "min.py", line 4, in main
os.environ['blah'] = 'bloo'
UnboundLocalError: local variable 'os' referenced before assignment
$
Because an import is also an assignment. If you do import os.path in your main method, you're making os a local name, and Python won't look in the global scope to see the os you've already imported.
# assigns 'os' to global namespace
import os
def main():
os.environ['blah'] = 'bloo' # <== which 'os'? python assumes local
# assigns 'os' to local namespace
import os.path
import does two things. First, it creates a module object. Second it gives a name to that newly created module object. Giving something a name is assignment, hence the error message "UnboundLocalError: local variable 'os' referenced before assignment".
Python has rules about the visibility of names and when they can be referenced. One of the rules is that there cannot be ambiguity in a function as to whether you mean to refer to a local name or a global name. If a local name is created anywhere inside a function that is the same as a global name, Python assumes that all references to that name inside the function refer to the local name (unless you specifically say otherwise).
Three possible fixes.
Drop the local import:
import os
def main():
os.environ['blah'] = 'bloo'
Drop the global import and move local import to the top of the function
def main():
import os.path
os.environ['blah'] = 'bloo'
Declare os global at the beginning of your function so that the first reference uses the global:
import os
def main():
global os
os.environ['blah'] = 'bloo'
import os.path
A note about imports.
# has the same effect as 'import module'
# creates a reference named 'module'
import module.submodule
# imports submodule and creates a reference to it named 'submodule'
from module import submodule
# imports submodule and creates a reference to it named 'x'
import module.submodule as x
I happens because you are trying to use the library 'os' before importing it.
You can do
def main():
import os
#now you can use os.environment
os.environ['blah'] = 'bloo'
#you don't need to import os.path as os is already imported
if __name__ == '__main__':
main()

Python globals var different inside class when imported

This is clearly a scope or import issue of some kind, but I can't figure it out. Something like:
classes.py
class Thing(object):
#property
def global_test(self):
return the_global
And then...
test.py
from classes import Thing
global the_global
the_global = 'foobar'
t = Thing()
t.global_test
:(
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "classes.py", line 4, in global_test
return the_global
NameError: global name 'the_global' is not defined
Any help would be great!
"global" in Python is a variable accessible in top level within module.
This message:
NameError: global name 'the_global' is not defined
raised within classes.py means you do not have a global named the_global within your classes.py file.
Python modules do not share global variables. (well, not in the way you want them to share)
The 'global' variables only defines a variable as global inside the scope of the module
where it is used. You can not use 'global' here to access a variable outside the module
scope of the 'classes' module.
The proper solution here if you have to deal with global defines or so: move the "global"
variables into a dedicated module and use a proper import statement to import the variables
into your 'classes' module.
myvars.py:
MY_GLOBAL_VAR = 42
classes.py:
import myvars
class Thing():
def method(self):
return myvars.MY_GLOBAL_VAR # if you need such a weird pattern for whatever reason

Getting "global name 'foo' is not defined" with Python's timeit

I'm trying to find out how much time it takes to execute a Python statement, so I looked online and found that the standard library provides a module called timeit that purports to do exactly that:
import timeit
def foo():
# ... contains code I want to time ...
def dotime():
t = timeit.Timer("foo()")
time = t.timeit(1)
print "took %fs\n" % (time,)
dotime()
However, this produces an error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in dotime
File "/usr/local/lib/python2.6/timeit.py", line 193, in timeit
timing = self.inner(it, self.timer)
File "<timeit-src>", line 6, in inner
NameError: global name 'foo' is not defined
I'm still new to Python and I don't fully understand all the scoping issues it has, but I don't know why this snippet doesn't work. Any thoughts?
Change this line:
t = timeit.Timer("foo()")
To this:
t = timeit.Timer("foo()", "from __main__ import foo")
Check out the link you provided at the very bottom.
To give the timeit module access to functions you define, you can pass a setup parameter which contains an import statement:
I just tested it on my machine and it worked with the changes.
With Python 3, you can use globals=globals()
t = timeit.Timer("foo()", globals=globals())
From the documentation:
Another option is to pass globals() to the globals parameter, which
will cause the code to be executed within your current global
namespace. This can be more convenient than individually specifying
imports
You can try this hack:
import timeit
def foo():
print 'bar'
def dotime():
t = timeit.Timer("foo()")
time = t.timeit(1)
print "took %fs\n" % (time,)
import __builtin__
__builtin__.__dict__.update(locals())
dotime()
t = timeit.Timer("foo()", "from __main__ import foo")
Since timeit doesn't have your stuff in scope.
add into your setup "import thisfile; "
then when you call the setup function myfunc() use "thisfile.myfunc()"
eg "thisfile.py"
def myfunc():
return 5
def testable(par):
pass
t=timeit.timeit(stmt="testable(v)",setup="import thisfile; v=thisfile.myfunc();").repeat(10)
print( t )

Categories