这与编译器在编译为字节码时基于不相关分支进行静态分析无关;这要简单得多。
Python有一个区分全局变量,闭包变量和局部变量的规则。在函数中分配的所有变量(包括隐式分配给参数的参数)都是局部变量(除非它们具有
globalor
nonlocal语句)。参考文档的“绑定和命名”及后续章节对此进行了说明。
这并不是要使解释器简单,而是要使规则足够简单以使人类读者通常可以直观地理解,并且在不直观时可以很容易地被人解决。(这对于此类情况尤其重要-
行为并非到处都是直观的,因此Python将规则保持足够简单,以使一旦学习,该情况仍然很明显。但是您肯定必须在此之前学习该规则是的,当然,大多数人是第一次对规则感到惊讶来学习该规则的…)
即使优化器足够聪明,可以完全删除与之相关的任何字节码
if False: ord=None,
ord但根据语言语义规则,仍必须是局部变量。
因此:
ord=函数中有一个,因此对的所有引用
ord都是对局部变量的引用,而不是碰巧具有相同名称的任何全局或非局部变量,因此您的代码是
UnboundLocalError。
许多人在不了解实际规则的情况下走了过来,而是使用了更简单的规则:变量是
- 如果可能,则为本地,否则为
- 如果可能,将其封闭,否则
- 如果是全局变量,则为全局变量,否则
- 内置(如果内置),否则
- 一个错误
尽管这种方法在大多数情况下都有效,但在某些情况下(例如这种情况)可能会产生误导。具有以Lisp样式完成LEGB范围界定的语言会看到它
ord不在本地名称空间中,因此会返回全局名称,但Python不会这样做。您可以说这
ord
是
在本地名称空间中,但是绑定到一个特殊的“未定义”值,它实际上接近幕后发生的事情,但这不是Python规则所说的,尽管这样做可能更直观简单的情况下,很难进行推理。
如果您好奇这是如何工作的,请执行以下 *** 作:
在CPython中,编译器将扫描您的函数以查找所有以标识符为目标的赋值,并将其存储在数组中。它删除全局变量和非局部变量。该数组最终将作为您的代码对象的数组
co_varnames,因此,假设您
ord是
co_varnames[1]。然后,对该变量的每次使用都会编译为
LOAD_FAST1或
STORE_FAST 1,而不是编译为
LOAD_NAME或
STORE_GLOBAL或其他 *** 作。解释时,这
LOAD_FAST1只是将框架的内容加载
f_locals[1]到堆栈中。它
f_locals以NULL指针数组而不是指向Python对象的指针开始,如果
LOAD_FAST加载了NULL指针,它将引发
UnboundLocalError。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)