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

问题描述 投票:0回答:3

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

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 进入函数之前评估的。

这背后的原因是什么?

python python-import python-module
3个回答
2
投票

在进入函数之前不会评估导入,但在执行之前会解析函数体以进行赋值。从函数体的开头开始,赋值中绑定的任何名称都被视为该函数的本地名称。由于

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()

1
投票

这里有一个更简单的例子:

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

原因是局部变量(如全局变量)是在编译时确定的,覆盖同名的全局变量,但仅在运行时绑定到值,因此如果在赋值之前访问它们,它们是已知的,但尚未定义(例如绑定)。 建议:永远不要使用属性(点表示法)遍历子模块。这是非常脆弱的。有时这些属性是存在的。下次他们不会。只是永远不要使用它们。始终导入最里面的模块(或成员)


0
投票

import xml.dom print(xml.dom.__name__)

使用这个:

from xml import dom
print(dom.__name__)

至于你的问题发生的原因,请记住

import foo.bar.baz
 的奇怪语义,它实际上设置 
foo

变量,而不是

baz
。然后所有Python的“赋值前引用”开始适用。
    

© www.soinside.com 2019 - 2024. All rights reserved.