I was wrestling with a weird "UnboundLocalError: local variable referenced before assignment" issue in a multi-submodule project I am working on and slimmed it down to this snippet (using the logging module from standard library):
import logging
def foo():
logging.info('foo')
def bar():
logging.info('bar')
if False:
import logging.handlers
# With these alternatives things work:
# import logging.handlers as logginghandlers
# from logging.handlers import SocketHandler
logging.basicConfig(level=logging.INFO)
foo()
bar()
Which has this output (I tried python 2.7 and 3.3):
INFO:root:foo
Traceback (most recent call last):
File "import-test01.py", line 16, in <module>
bar()
File "import-test01.py", line 7, in bar
logging.info('bar')
UnboundLocalError: local variable 'logging' referenced before assignment
Apparently the presence of an import statement inside a function hides already existing variables with the same name in the function scope, even if the import is not executed.
This feels counter-intuitive and non-pythonic. I tried to find some information or documentation about this, without much success thus far. Has someone more information/insights about this behaviour?
thanks
-
One Way to deal with masking issue is by using 'as' when importing.... if False: import logging.handlers as handlerNikhil Rupanawar– Nikhil Rupanawar2014年06月19日 13:24:34 +00:00Commented Jun 19, 2014 at 13:24
-
@Stefaan It looks like good reason to follow the PEP 008 advice on imports: "Imports are always put at the top of the file, just after any module comments and docstrings, and before module globals and constants." I know, there are often reasons to break the rules (at your own risk).Jan Vlcinsky– Jan Vlcinsky2014年06月19日 13:27:20 +00:00Commented Jun 19, 2014 at 13:27
2 Answers 2
The problem you're having is just a restatement of the same ol' way python deals with locals masking globals.
to understand it, import foo is (approximately) syntactic sugar for:
foo = __import__("foo")
thus, your code is:
x = 1
def bar():
print x
if False:
x = 2
since the name logging appears on the left side of an assignment statement inside bar, it is taken to be a local variable reference, and so python won't look for a global by the same name in that scope, even though the line that sets it can never be called.
The usual workarounds for dealing with globals apply: use another name, as you have found, or:
def bar():
global logging
logging.info('bar')
if False:
import logging.handlers
so that python won't think logging is local.
Comments
By that import statement you introduce logging as a local variable in the function and you call that local variable before it's initialized.
def bar():
import logging.handlers
print locals()
>>> foo()
{'logging': <module 'logging' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.pyc'>}
3 Comments
locals() after execution of the import statement, which is totally as expected. My problem is that "logging" is not in locals() before the import statement (not necessarily to be executed), it is in globals(), and still I get UnboundLocalError, as if was a still undefined local variable.