<p><strong>TL;DR</strong>:这种行为从python2.1<a href="https://docs.python.org/3/whatsnew/2.1.html#pep-227-nested-scopes" rel="noreferrer">PEP 227: Nested Scopes</a>就已经存在了,并且在那时就已经知道了。如果一个名称在类主体中被赋值(比如<code>y</code>),那么它就被认为是一个局部/全局变量;如果它没有被赋值给(<code>x</code>),那么它也可能指向一个闭包单元。词法变量不会作为类主体的局部/全局名称显示。在</p>
<hr/>
<p>在Python3.4上,<code>dis.dis(func)</code>显示了以下内容:</p>
<pre><code>>>> dis.dis(func)
4 0 LOAD_CONST 1 ('xlocal')
3 STORE_DEREF 0 (x)
5 6 LOAD_CONST 2 ('ylocal')
9 STORE_FAST 0 (y)
6 12 LOAD_BUILD_CLASS
13 LOAD_CLOSURE 0 (x)
16 BUILD_TUPLE 1
19 LOAD_CONST 3 (<code object C at 0x7f083c9bbf60, file "test.py", line 6>)
22 LOAD_CONST 4 ('C')
25 MAKE_CLOSURE 0
28 LOAD_CONST 4 ('C')
31 CALL_FUNCTION 2 (2 positional, 0 keyword pair)
34 STORE_FAST 1 (C)
37 LOAD_CONST 0 (None)
40 RETURN_VALUE
</code></pre>
<p><code>LOAD_BUILD_CLASS</code>在堆栈上加载<code>builtins.__build_class__</code>;这是用参数<code>__build_class__(func, name)</code>调用的;其中<code>func</code>是类主体,<code>name</code>是{<cd9>}。类主体是函数<code>func</code>的常量#3:</p>
^{pr2}$
<p>在类主体中,<code>x</code>是用<code>LOAD_CLASSDEREF</code>(15)访问的,而<code>y</code>是用<code>LOAD_NAME</code>(25)加载的。<code>LOAD_CLASSDEREF</code>是一个python3.4+操作码,用于从类主体内的闭包单元加载值(在以前的版本中,使用了泛型的^{<cd16>);而{<cd14>}用于从<em>局部变量</em>加载值,然后从<em>全局</em>加载值。然而,闭合单元既不显示为局部的,也不显示为全局的。在</p>
<p>现在,由于名称<code>y</code>存储在类主体(35)中,因此它一直被用作本地/全局名称而不是闭包单元。
闭包单元不显示为类主体的局部变量。在</p>
<p>这种行为是正确的<a href="https://mail.python.org/pipermail/python-dev/2002-April/023428.html" rel="noreferrer">ever since implementing PEP 227 - nested scopes</a>。当时BDFL表示,这不应该是固定的,因此这13年来一直如此。在</p>
<hr/>
<p>自PEP 227以来的唯一变化是在Python3中添加了<code>nonlocal</code>;如果在类主体中使用它,那么类主体可以设置包含范围内单元格的值:</p>
<pre><code>x = "xtop"
y = "ytop"
def func():
x = "xlocal"
y = "ylocal"
class C:
nonlocal y # y here now refers to the outer variable
print(x)
print(y)
y = 1
print(y)
print(C.y)
func()
</code></pre>
<p>现在的输出是</p>
<pre><code>xlocal
ylocal
1
Traceback (most recent call last):
File "test.py", line 15, in <module>
func()
File "test.py", line 13, in func
print(C.y)
AttributeError: type object 'C' has no attribute 'y'
</code></pre>
<p>也就是说,<code>print(y)</code>读取包含范围的单元格<code>y</code>的值,<code>y = 1</code>设置该单元格中的值;在这种情况下,没有为类<code>C</code>创建属性。在</p>