Python 为什么是';作战需求文件';在这里被视为未赋值变量?
我希望这不是一个重复(同时,考虑到有这么多的问题有这样的错误,很难说出来,但哪些是基本的错误),但我不明白这里发生了什么Python 为什么是';作战需求文件';在这里被视为未赋值变量?,python,variables,Python,Variables,我希望这不是一个重复(同时,考虑到有这么多的问题有这样的错误,很难说出来,但哪些是基本的错误),但我不明白这里发生了什么 def f(): c = ord('a') f() 运行,无错误(ord将字符转换为ASCII码,它是内置的)。现在: 同时运行,无错误(ord未被覆盖,条件始终为false)。现在: 我得到(在c=ord('a')的第行) 似乎只要引用左侧操作数就可以使其成为局部变量,即使代码没有运行 显然我可以解决这个问题,但我非常惊讶,因为python的动态特性允许您定义一
def f():
c = ord('a')
f()
运行,无错误(ord
将字符转换为ASCII码,它是内置的)。现在:
同时运行,无错误(ord
未被覆盖,条件始终为false)。现在:
我得到(在c=ord('a')
的第行)
似乎只要引用左侧操作数就可以使其成为局部变量,即使代码没有运行
显然我可以解决这个问题,但我非常惊讶,因为python的动态特性允许您定义一个变量,比如整数,然后在下一行将其定义为字符串
这似乎与
显然,当编译成字节码时,解释器仍然会记录未到达的分支,但是到底发生了什么呢
(在Python2.7和Python3.4上测试)这与编译器在编译成字节码时基于不相关的分支进行静态分析无关;这要简单得多 Python有一个区分全局变量、闭包变量和局部变量的规则。函数中分配给的所有变量(包括隐式分配给的参数)都是局部变量(除非它们具有
global
或nonlocal
语句)。这将在参考文档中及其后的章节中进行解释
这不是让解释器保持简单,而是让规则保持足够简单,使其对人类读者来说通常是直观的,当它不是直观的时候,人类可以很容易地解决。(这对于这种情况尤其重要,因为行为不可能处处都是直观的,所以Python将规则保持得足够简单,一旦你学会了,这种情况仍然是显而易见的。但你肯定必须先学会规则,然后才知道这是真的。当然,大多数人在第一次学习规则时都会感到惊讶……)
即使优化器足够聪明,可以完全删除与相关的任何字节码(如果为False:ord=None
),根据语言语义规则,ord
仍然必须是局部变量
因此:在函数中有一个ord=
,因此对ord
的所有引用都是对局部变量的引用,而不是对碰巧具有相同名称的任何全局或非局部变量的引用,因此您的代码是一个UnboundLocalError
许多人在不知道实际规则的情况下就成功了,而是使用了一个更简单的规则:变量是
- 如果可能,则为本地,否则为
- 如果可能,则将其封闭,否则
- 如果是全局的,则为全局的,否则为全局的
- 如果是内置的,则为内置的,否则为
- 错误
ord
不在本地名称空间中,因此返回全局名称空间,但Python不会这样做。您可以说,ord
位于本地名称空间中,但绑定到一个特殊的“未定义”值,这实际上接近于在封面下发生的情况,但这不是Python的规则所说的,而且,虽然对于简单的情况可能更直观,但很难进行推理
如果您想知道这在封面下是如何工作的:
在CPython中,编译器扫描您的函数以查找以标识符为目标的所有赋值,并将它们存储在数组中。它删除全局和非局部变量。这个数组最终成为代码对象的
co\u varnames
,因此假设您的ord
是co\u varnames[1]
。然后,该变量的每次使用都会被编译为LOAD\u FAST 1
或STORE\u FAST 1
,而不是LOAD\u NAME
或STORE\u GLOBAL
或其他操作。当解释时,LOAD_FAST 1
仅将帧的f_局部变量[1]
加载到堆栈上。f_locals
以空指针数组开始,而不是指向Python对象的指针,如果LOAD\u FAST
加载空指针,它会引发UnboundLocalError
以演示编译器的运行情况:
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
使用用于局部变量的LOAD\u FAST
访问a
如果在功能外将ord
设置为无,则使用LOAD\u 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
这已经被问过很多次了。Python中的编译器静态地确定局部变量。分配给的每个名称在编译时都标记为局部变量。将
global ord
放在函数前面可以避免错误。@彼得伍德我不是在问如何修复它,我是在问一个解释。@Jean-Françoisfare我是在向部分解释添加缺失的信息。这是一匹礼物马。无论如何,谢谢你。我知道你想帮忙。使规则有些不直观的部分是,即使ord+=1
也会呈现ord
一个局部变量,尽管除非函数中有另一个ord
赋值,否则这是没有意义的。@SvenMarnach这正是“足够简单,以便在不直观的情况下轻松解决”这一部分非常重要。你必须学习规则,但一旦学习,就很难出错。值得一提的是,我认为这一规则是必要的,因为局部变量是通过索引而不是字典查找(用于全局变量)来访问的。否则,Python可以简单地执行动态查找–首先查找本地范围,如果找不到名称,则查找封闭范围。由于本地变量是按索引而不是按名称查找的,因此编译器需要静态地确定哪些变量是本地变量
UnboundLocalError: local variable 'ord' referenced before assignment
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
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