Python:惰性子模块导入使父模块无效 > UnboundLocalError - 但为什么呢?

Python: lazy submodule imports invalidate parent modules > UnboundLocalError - But why?

考虑以下代码: 我在我的代码中使用了一个子模块,并且只在一个函数中使用了一个特殊的子模块(加载起来可能非常繁重)所以我懒得导入:

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

原因是局部变量(和全局变量一样)在编译时确定,覆盖同名的全局变量,但只绑定到值at run-time,因此如果在赋值之前访问它们,则它们是已知的,但尚未定义(例如绑定)。