Python函数内的导入隐藏了现有变量
我在一个包含多个子模块的项目中遇到了一个奇怪的问题,错误提示是“UnboundLocalError: local variable referenced before assignment”。我把问题简化到这段代码(使用了标准库中的日志模块):
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()
运行这段代码后,输出结果是这样的(我尝试了Python 2.7和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
看起来在一个函数内部如果有一个导入语句,就会隐藏掉函数作用域内已经存在的同名变量,即使这个导入语句没有被执行。
这让我觉得有点反直觉,也不太符合Python的风格。我尝试查找一些相关的信息或文档,但到目前为止没有太多收获。有没有人对这种行为有更多的了解或见解?
谢谢!
2 个回答
1
通过这个 import
语句,你在函数里引入了一个叫 logging
的局部变量,但你在这个变量还没初始化之前就调用了它。
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'>}
4
你遇到的问题其实就是 Python 处理局部变量和全局变量时的一个常见情况。
要理解这个问题,import foo
大致上可以看作是这样的一种写法:
foo = __import__("foo")
所以,你的代码实际上是:
x = 1
def bar():
print x
if False:
x = 2
因为在 bar 函数内部,logging
出现在赋值语句的左边,所以 Python 会把它当作局部变量来处理。这就意味着,即使有一个同名的全局变量,Python 也不会在这个范围内去查找它,尽管设置这个局部变量的那行代码根本不会被执行。
处理全局变量的常见解决办法依然适用:可以使用其他名字,正如你发现的那样,或者:
def bar():
global logging
logging.info('bar')
if False:
import logging.handlers
这样 Python 就不会把 logging
当作局部变量了。