<p>关于Python的第三次没有一个彻底的答案,所以我在这里做了一个回答。这里描述的大部分内容在Python 3文档的<a href="https://docs.python.org/3/reference/executionmodel.html#resolution-of-names" rel="nofollow noreferrer">4.2.2 Resolution of names</a>中有详细说明。</p>
<p>如其他答案所述,有4个基本作用域,LEGB,用于本地、封闭、全局和内置。除此之外,还有一个特殊的作用域,即类主体,它不包含类内定义的方法的封闭作用域;类主体中的任何赋值都会使从那里开始的变量绑定到类主体中。</p>
<p>特别是,<strong>no</strong>block语句,除了<code>def</code>和<code>class</code>之外,还创建一个变量作用域。在Python 2中,列表理解不创建变量作用域,但是在Python 3中,列表理解中的循环变量是在新作用域中创建的。</p>
<p>展示班集体的特点</p>
<pre><code>x = 0
class X(object):
y = x
x = x + 1 # x is now a variable
z = x
def method(self):
print(self.x) # -> 1
print(x) # -> 0, the global x
print(y) # -> NameError: global name 'y' is not defined
inst = X()
print(inst.x, inst.y, inst.z, x) # -> (1, 0, 1, 0)
</code></pre>
<p>因此,与在函数体中不同,您可以将变量重新分配给类体中的相同名称,以获得具有相同名称的类变量;进一步查找此名称将解析
改为类变量。</p>
<hr/>
<p>对于许多Python新手来说,一个更大的惊喜是<code>for</code>循环没有创建变量作用域。在Python 2中,列表理解也不创建作用域(而生成器和dict理解也创建作用域!)相反,它们会泄漏函数或全局范围中的值:</p>
<pre><code>>>> [ i for i in range(5) ]
>>> i
4
</code></pre>
<p>这种理解可以被用作在Python 2中的lambda表达式中创建可修改变量的一种狡猾的方法(如果您愿意的话,可能会很糟糕)——lambda表达式确实创建了一个变量作用域,就像<code>def</code>语句那样,但是在lambda中不允许使用任何语句。赋值是Python中的一个语句,这意味着lambda中不允许变量赋值,但是列表理解是一个表达式。。。</p>
<p>这种行为在Python3中已被修复-没有理解表达式或生成器泄漏变量。</p>
<hr/>
<p>全局实际上意味着模块作用域;主要的python模块是<code>__main__</code>;所有导入的模块都可以通过<code>sys.modules</code>变量访问;要访问<code>__main__</code>,可以使用<code>sys.modules['__main__']</code>,或者<code>import __main__</code>;在那里访问和分配属性是完全可以接受的;它们将在主模块的全局范围内显示为变量。</p>
<hr/>
<p>如果在当前作用域(类作用域除外)中分配了名称,则该名称将被视为属于该作用域,否则将被视为属于分配给变量的任何封闭作用域(可能尚未分配或根本没有分配),或最终属于全局作用域。如果变量被视为局部变量,但尚未设置或已被删除,则读取变量值将导致<code>UnboundLocalError</code>,这是<code>NameError</code>的子类。</p>
<pre><code>x = 5
def foobar():
print(x) # causes UnboundLocalError!
x += 1 # because assignment here makes x a local variable within the function
# call the function
foobar()
</code></pre>
<p>作用域可以声明它明确希望修改全局(模块作用域)变量,并使用全局关键字:</p>
<pre><code>x = 5
def foobar():
global x
print(x)
x += 1
foobar() # -> 5
print(x) # -> 6
</code></pre>
<p>这也是可能的,即使它被隐藏在封闭范围内:</p>
<pre><code>x = 5
y = 13
def make_closure():
x = 42
y = 911
def func():
global x # sees the global value
print(x, y)
x += 1
return func
func = make_closure()
func() # -> 5 911
print(x, y) # -> 6 13
</code></pre>
<p>在Python2中,没有简单的方法可以修改封闭范围中的值;通常这是通过具有可变值来模拟的,例如长度为1:</p>
<pre><code>def make_closure():
value = [0]
def get_next_value():
value[0] += 1
return value[0]
return get_next_value
get_next = make_closure()
print(get_next()) # -> 1
print(get_next()) # -> 2
</code></pre>
<p>然而在python 3中,<code>nonlocal</code>来拯救:</p>
<pre><code>def make_closure():
value = 0
def get_next_value():
nonlocal value
value += 1
return value
return get_next_value
get_next = make_closure() # identical behavior to the previous example.
</code></pre>
<p><a href="https://docs.python.org/3/reference/simple_stmts.html#the-nonlocal-statement" rel="nofollow noreferrer">^{<cd12>} documentation</a>说</p>
<blockquote>
<p>Names listed in a nonlocal statement, unlike those listed in a global statement, must refer to pre-existing bindings in an enclosing scope (the scope in which a new binding should be created cannot be determined unambiguously).</p>
</blockquote>
<p>即<code>nonlocal</code>始终指名称已绑定的最里面的外部非全局作用域(即分配给,包括用作<code>for</code>目标变量、在<code>with</code>子句中或用作函数参数)。</p>
<hr/>
<p>任何不被视为当前作用域或任何封闭作用域本地的变量都是全局变量。全局名称在模块全局字典中查找;如果找不到,则从内置模块中查找全局;模块的名称从python 2更改为python 3;在python 2中,它是<code>__builtin__</code>,在python 3中,它现在被称为<code>builtins</code>。如果指定给buil的属性tins模块,此后它将作为可读的全局变量对任何模块可见,除非该模块使用自己的同名全局变量隐藏它们。</p>
<hr/>
<p>读取内置模块也很有用;假设您希望在文件的某些部分使用python 3样式的打印函数,但文件的其他部分仍然使用<code>print</code>语句。在Python2.6-2.7中,您可以通过以下方法获得Python3<code>print</code>函数:</p>
<pre><code>import __builtin__
print3 = __builtin__.__dict__['print']
</code></pre>
<p>实际上,<code>from __future__ import print_function</code>并没有在Python 2中的任何地方导入<code>print</code>函数-相反,它只是禁用当前模块中<code>print</code>语句的解析规则,像处理任何其他变量标识符一样处理<code>print</code>,从而允许在内置项中查找<code>print</code>函数。</p>