为什么“ord"在这里被视为未分配的变量? [英] why is 'ord' seen as an unassigned variable here?
问题描述
我希望它不是重复的(同时,鉴于此类错误的问题数量很多,因此很难判断,但哪些是基本错误),但我不明白这里发生了什么.
I hope it's not a duplicate (and at the same time it's difficult to tell, given the amount of questions with such errors, but which are basic mistakes), but I don't understand what happens here.
def f():
c = ord('a')
f()
运行,没有错误(ord
将字符转换为 ASCII 代码,它是内置的).现在:
runs, no error (ord
converts character to ASCII code, it's a built-in). Now:
if False:
ord = None
def f():
c = ord('a')
f()
也运行,没有错误(ord
不会被覆盖,条件总是假的).现在:
Also runs, no error (ord
isn't overwritten, condition is always false). Now:
def f():
if False:
ord = None
c = ord('a')
f()
我得到(在 c = ord('a')
处)
UnboundLocalError: local variable 'ord' referenced before assignment
似乎只是引用左侧操作数使其成为局部变量,即使代码没有运行.
It seems that just referencing a left side operand makes it a local variable, even if the code is not run.
显然我可以解决这个问题,但我很非常惊讶,因为 python 的动态方面允许你定义一个变量,比如一个整数,并在下一行将它定义为一个字符串.
Obviously I can workaround this, but I was very surprised, given that the dynamic aspect of python allows you to define a variable like being an integer, and at the next line define it as a string.
显然解释器在编译为字节码时仍会记录未到达的分支,但究竟发生了什么?
Apparently the interpreter still takes notes of unreached branches when compiling to bytecode, but what happens exactly?
(在 Python 2.7 和 Python 3.4 上测试)
(tested on Python 2.7 and Python 3.4)
推荐答案
这不是编译器在编译为字节码时根据无关分支进行静态分析;就简单多了.
It's not about the compiler doing a static analysis based on unrelated branches when compiling to bytecode; it's much simpler.
Python 有区分全局、闭包和局部变量的规则.在函数中分配的所有变量(包括隐式分配的参数)都是局部变量(除非它们具有 global
或 nonlocal
语句).这在 绑定和命名 和后续部分中进行了解释参考文档.
Python has a rule for distinguishing global, closure, and local variables. All variables that are assigned to in the function (including parameters, which are assigned to implicitly), are local variables (unless they have a global
or nonlocal
statement). This is explained in Binding and Naming and subsequent sections in the reference documentation.
这不是要让解释器保持简单,而是要让规则足够简单,以至于它通常对人类读者来说是直观的,并且在不直观时可以很容易地由人类解决.(这对于这样的情况尤其重要——行为不可能在任何地方都是直观的,所以 Python 保持规则足够简单,一旦你学会了,这样的情况仍然很明显.但你绝对必须在此之前学习规则是的.而且,当然,大多数人是通过第一次就惊讶于它来学习规则的......)
This isn't about keeping the interpreter simple, it's about keeping the rule simple enough that it's usually intuitive to human readers, and can easily be worked out by humans when it isn't intuitive. (That's especially important for cases like this—the behavior can't be intuitive everywhere, so Python keeps the rule simple enough that, once you learn it, cases like this are still obvious. But you definitely do have to learn the rule before that's true. And, of course, most people learn the rule by being surprised by it the first time…)
即使使用足够聪明的优化器来完全删除与 if False: ord=None
相关的任何字节码,根据语言规则,ord
仍然必须是局部变量语义.
Even with an optimizer smart enough to completely remove any bytecode related to if False: ord=None
, ord
must still be a local variable by the rules of the language semantics.
所以:您的函数中有一个 ord =
,因此所有对 ord
的引用都是对局部变量的引用,而不是任何具有相同属性的全局或非局部变量名称,因此您的代码是 UnboundLocalError
.
So: there's an ord =
in your function, therefore all references to ord
are references to a local variable, not any global or nonlocal that happens to have the same name, and therefore your code is an UnboundLocalError
.
很多人在不知道实际规则的情况下,而是使用更简单的规则:变量是
Many people get by without knowing the actual rule, and instead use an even simpler rule: a variable is
- 本地,如果可能的话,否则
- 尽可能封闭,否则
- 如果在全局变量中,则为全局,否则为
- 内置,如果它是内置的,否则
- 错误
虽然这适用于大多数情况,但在某些情况下可能会有点误导——比如这个.具有 LEGB 范围完成 Lisp 风格的语言会看到 ord
不在本地名称空间中,因此返回全局名称,但 Python 不会这样做.你可以说 ord
is 在本地命名空间中,但绑定到一个特殊的未定义"值,这实际上接近幕后发生的事情,但事实并非如此Python 的规则是这样说的,虽然对于简单的情况它可能更直观,但更难推理.
While this works for most cases, it can be a bit misleading in some cases—like this one. A language with LEGB scoping done Lisp-style would see that ord
isn't in the local namespace, and therefore return the global, but Python doesn't do that. You could say that ord
is in the local namespace, but bound to a special "undefined" value, and that's actually close to what happens under the covers, but that's not what the rules of Python say, and, while it may be more intuitive for simple cases, it's harder to reason through.
如果您想知道它是如何工作的:
If you're curious how this works under the covers:
在 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
.
In CPython, the compiler scans your function to find all assignments with an identifier as a target, and stores them in an array. It removes global and nonlocal variables. This arrays ends up as your code object's co_varnames
, so let's say your ord
is co_varnames[1]
. Every use of that variable then gets compiled to a LOAD_FAST 1
or STORE_FAST 1
, instead of a LOAD_NAME
or STORE_GLOBAL
or other operation. That LOAD_FAST 1
just loads the frame's f_locals[1]
onto the stack when interpreted. That f_locals
starts off as an array of NULL pointers instead of pointers to Python objects, and if a LOAD_FAST
loads a NULL pointer, it raises UnboundLocalError
.
这篇关于为什么“ord"在这里被视为未分配的变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!