>>> from env_helper import info; info()
页面更新时间: 2024-01-17 14:49:27
运行环境:
    Linux发行版本: Debian GNU/Linux 12 (bookworm)
    操作系统内核: Linux-6.1.0-17-amd64-x86_64-with-glibc2.36
    Python版本: 3.11.2

8.5. 作用域

变量到底是什么呢?可将其视为指向值的名称。因此,执行赋值语句 x = 1 后,名称 x 指向值 1 。 这几乎与使用字典时一样(字典中的键指向值),只是你使用的是“看不见”的字典。 实际上, 这种解释已经离真相不远。有一个名为 vars() 的内置函数,它返回这个不可见的字典:

>>> x = 1
>>> scope = vars()
>>> scope['x']
1

这个值可以进行赋值:

>>> scope['x'] += 1
>>> x
2

警告 : 一般而言,不应修改 vars 返回的字典,因为根据Python官方文档的说法,这样做的结果是不确定的。换而言之,可能得不到你想要的结果。

这种“看不见的字典”称为命名空间或作用域。那么有多少个命名空间呢?除全局作用域外, 每个函数调用都将创建一个。

>>> def foo():
>>>     x = 42
>>> x = 1
>>> foo()
>>> x
1

在这里,函数 foo 修改(重新关联)了变量 x ,但当你最终查看时,它根本没变。 这是因为调用 foo 时创建了一个新的命名空间,供 foo 中的代码块使用。 赋值语句 x = 42 是在这个内部作用域(局部命名空间)中执行的,不影响外部(全局)作用域内的 x 。 在函数内使用的变量称为局部变量(与之相对的是全局变量)。 参数类似于局部变量,因此参数与全局变量同名不会有任何问题。

>>> def output(x):
>>>     print(x)
>>> x = 1
>>> y = 2
>>> output(y)
2

到目前为止一切顺利。但如果要在函数中访问全局变量呢?如果只是想读取这种变量的值 (不重新关联它),通常不会有任何问题。

>>> def combine(parameter):
>>>     print(parameter + external)
>>>
>>> external = 'berry'
>>> combine('Shrub')
Shrubberry

警告 像这样访问全局变量是众多bug的根源。务必慎用全局变量。

8.5.1. “遮盖”的问题

读取全局变量的值通常不会有问题,但还是存在出现问题的可能性。 如果有一个局部 变量或参数与你要访问的全局变量同名,就无法直接访问全局变量,因为它被局部变量遮 住了。

如果需要,可使用函数 globals 来访问全局变量。 这个函数类似于 vars() ,返回一个包含全 局变量的字典。( locals 返回一个包含局部变量的字典。)

例如,在前面的示例中,如果有一个名为parameter的全局变量,就无法在函数 combine 中访问它,因为有一个与之同名的参数。 然而,必要时可使用 globals()['parameter'] 来访问它。

>>> def combine(parameter):
>>>     print(parameter + globals()['parameter'])
>>> parameter = 'berry'
>>> combine('Shrub')
Shrubberry

重新关联全局变量(使其指向新值)是另一码事。 在函数内部给变量赋值时,该变量默认为 局部变量,除非你明确地告诉Python它是全局变量。 那么如何将这一点告知Python呢?

>>> x = 1
>>> def change_global():
>>>     global x
>>>     x = x + 1
>>> change_global()
>>> x
2

8.5.2. 作用域嵌套

Python函数可以嵌套,即可将一个函数放在另一个函数内,如下所示:

>>> def foo():
>>>     def bar():
>>>         print("Hello, world!")
>>>     bar()

嵌套通常用处不大,但有一个很突出的用途:使用一个函数来创建另一个函数。这意味 着可像下面这样编写函数:

>>> def multiplier(factor):
>>>     def multiplyByFactor(number):
>>>         return number * factor
>>>     return multiplyByFactor

在这里,一个函数位于另一个函数中,且外面的函数返回里面的函数。 也就是返回一个 函数,而不是调用它。重要的是,返回的函数能够访问其定义所在的作用域。 换而言之,它 携带着自己所在的环境(和相关的局部变量)!

每当外部函数被调用时,都将重新定义内部的函数,而变量 factor 的值也可能不同。 由于 Python 的嵌套作用域,可在内部函数中访问这个来自外部局部作用域(multiplier)的变量,如下所示:

>>> double = multiplier(2)
>>> double(5)
10
>>> triple = multiplier(3)
>>> triple(3)
9
>>> multiplier(5)(4)
20

multiplyByFactor 这样存储其所在作用域的函数称为闭包。 通常,不能给外部作用域内的变量赋值,但如果一定要这样做,可使用关键字 nonlocal 。 这个关键字的用法与 global 很像,让你能够给外部作用域(非全局作用域)内的变量赋值。