Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python import inside function hides existing variable

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

like image 220
Stefaan Avatar asked Jun 19 '14 12:06

Stefaan


1 Answers

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.

like image 150
SingleNegationElimination Avatar answered Oct 11 '22 17:10

SingleNegationElimination