考虑以下代码: 我在我的代码中使用了一个子模块,并在一个函数中使用了一个特殊的子模块(可能加载起来非常繁重),所以我懒惰地导入:
import xml.etree
def y():
print(xml.etree.__name__)
def x():
print(xml.etree.__name__)
import xml.dom
print(xml.dom.__name__)
if __name__ == "__main__":
y()
x()
这将导致
xml
被解除绑定:
UnboundLocalError: local variable 'xml' referenced before assignment
(是的,要修复它,我可以在函数内向上移动导入或执行
from xml import dom
)
我很想知道这里发生了什么。
显然导入是在 Python 进入函数之前评估的。
这背后的原因是什么?
在进入函数之前不会评估导入,但在执行之前会解析函数体以进行赋值。从函数体的开头开始,赋值中绑定的任何名称都被视为该函数的本地名称。由于
import
是一种隐式赋值形式,因此名称 xml
就成为函数的局部名称。但您在 import
作业之前使用它。所以这个错误实际上是相当明显的。
要修复它,您可以按照您所说的操作或简单地使用
global
:
import xml.etree
def y():
print(xml.etree.__name__)
def x():
global xml
print(xml.etree.__name__)
import xml.dom
print(xml.dom.__name__)
if __name__ == "__main__":
y()
x()
这里有一个更简单的例子:
a = 1
def foo():
print(a) # works
def bar():
print(a) # gives error
a = 2
print(a)
foo()
bar()
这给出:
1
UnboundLocalError: local variable 'a' referenced before assignment
原因是局部变量(如全局变量)是在编译时确定的,覆盖同名的全局变量,但仅在运行时绑定到值,因此如果在赋值之前访问它们,它们是已知的,但尚未定义(例如绑定)。 建议:永远不要使用属性(点表示法)遍历子模块。这是非常脆弱的。有时这些属性是存在的。下次他们不会。只是永远不要使用它们。始终导入最里面的模块(或成员)
import xml.dom
print(xml.dom.__name__)
使用这个:
from xml import dom
print(dom.__name__)
至于你的问题发生的原因,请记住
import foo.bar.baz
的奇怪语义,它实际上设置
foo
变量,而不是
baz
。然后所有Python的“赋值前引用”开始适用。