我希望它不是重复的(同时很难说,考虑到这些错误的问题数量,但这些都是基本的错误),但我不会这样做。了解这里发生了什么。
def f():
c = ord('a')
f()
运行,没有错误(ord
将字符转换为ASCII码,它是内置的)。现在:
if False:
ord = None
def f():
c = ord('a')
f()
同样运行,没有错误(ord
未被覆盖,条件始终为false)。现在:
def f():
if False:
ord = None
c = ord('a')
f()
我得到(c = ord('a')
)
UnboundLocalError: local variable 'ord' referenced before assignment
似乎只引用左侧操作数使其成为局部变量,即使代码未运行。
显然我可以解决这个问题,但是我感到很惊讶,因为python的动态方面允许你定义一个变量,就像是一个整数,并在下一行将它定义为一个字符串
似乎与What's the scope of a variable initialized in an if statement?
有关显然,解释器在编译为字节码时仍会记录未到达的分支,但究竟会发生什么?
(在Python 2.7和Python 3.4上测试)
答案 0 :(得分:13)
编译到字节码时,编译器是否根据不相关的分支进行静态分析;它简单得多。
Python有一个区分全局变量,闭包变量和局部变量的规则。在函数中分配给的所有变量(包括隐式分配的参数)都是局部变量(除非它们有global
或nonlocal
语句)。这在Binding and Naming以及参考文档中的后续部分中进行了解释。
这不是关于保持翻译简单,而是保持规则足够简单以至于人类读者通常是直观的,并且很容易被人类解决,当它不是&# 39;直观。 (这对于这样的情况尤其重要 - 行为在任何地方都可以是直观的,因此Python保持规则足够简单,一旦你学会了,这样的情况仍然很明显。但你肯定有在那之前学习规则是正确的。当然,大多数人第一次被它惊讶地学习规则......)
即使优化器足够聪明,可以完全删除与if False: ord=None
相关的任何字节码,ord
仍然必须是语言语义规则的局部变量。
所以:你的函数中有ord =
,因此对ord
的所有引用都是对局部变量的引用,而不是任何碰巧具有相同名称的全局或非本地变量,以及因此,您的代码为UnboundLocalError
。
许多人在不知道实际规则的情况下过关,而是使用更简单的规则:变量
虽然这适用于大多数情况,但在某些情况下可能会有点误导 - 就像这样。 LEGB范围完成Lisp风格的语言会看到ord
不在本地命名空间中,因此返回全局,但Python不会这样做。您可以说本地命名空间中ord
是,但绑定到特殊的" undefined"价值,这实际上接近封底下发生的事情,但这不是Python的规则所说的,虽然对于简单的案例可能更直观,但它更难以实现理由通过。
如果你好奇这是如何运作的:
在CPython中,编译器会扫描您的函数以查找标识符作为目标的所有赋值,并将它们存储在数组中。它删除了全局变量和非局部变量。此数组最终作为您的代码对象co_varnames
,因此请说明您的ord
为co_varnames[1]
。然后,对该变量的每次使用都会编译为LOAD_FAST 1
或STORE_FAST 1
,而不是LOAD_NAME
或STORE_GLOBAL
或其他操作。 LOAD_FAST 1
只会在解释时将框架f_locals[1]
加载到堆栈中。 f_locals
以NULL指针数组开始,而不是指向Python对象的指针,如果LOAD_FAST
加载NULL指针,则会引发UnboundLocalError
。
答案 1 :(得分:2)
只是为了演示编译器的用途:
def f():
if False:
ord = None
c = ord('a')
4 0 LOAD_FAST 0 (ord)
3 LOAD_CONST 1 ('a')
6 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
9 STORE_FAST 1 (c)
12 LOAD_CONST 0 (None)
15 RETURN_VALUE
访问a
正在使用LOAD_FAST
,用于本地变量。
如果您在功能之外将ord
设置为无,则使用LOAD_GLOBAL
代替:
if False:
ord = None
def f():
c = ord('a')
4 0 LOAD_GLOBAL 0 (ord)
3 LOAD_CONST 1 ('a')
6 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
9 STORE_FAST 0 (c)
12 LOAD_CONST 0 (None)
15 RETURN_VALUE