Python中的范围解析 | LEGB 规则
命名空间:命名空间是名称映射到对象的容器,它们用于避免在不同命名空间中存在相同名称的情况下发生混淆。它们由模块、函数、类等创建。
范围:范围定义了必须搜索命名空间以获得名称到对象(变量)的映射的层次顺序。它是一个存在变量并从中引用它们的上下文。它定义了变量的可访问性和生命周期。让我们举一个简单的例子,如下所示:
pi = 'outer pi variable'
def print_pi():
pi = 'inner pi variable'
print(pi)
print_pi()
print(pi)
输出:
inner pi variable
outer pi variable
上面的程序给出了不同的输出,因为同一个变量名pi驻留在不同的命名空间中,一个在函数print_pi中,另一个在上层。执行print_pi()时,将打印“内部 pi 变量”,因为这是函数命名空间内的pi值。当在外部命名空间中引用pi时,将打印值“外部 pi 变量”。从上面的例子中,我们可以猜测肯定有一个规则被遵循,以便决定必须从哪个命名空间中选择一个变量。通过 LEGB 规则进行范围解析:
在Python中, LEGB 规则用于决定搜索命名空间以进行范围解析的顺序。
下面按层次结构列出范围(从最高到最低/从最窄到最宽):
- Local(L):在函数/类内部定义
- Enclosed(E):定义在封闭函数内部(嵌套函数概念)
- Global(G):定义在最上层
- 内置(B): Python内置模块中的保留名称
本地范围:
局部作用域是指在当前函数中定义的变量。始终,函数会首先在其局部作用域中查找变量名。只有当它在那里没有找到它时,才会检查外部范围。
# Local Scope
pi = 'global pi variable'
def inner():
pi = 'inner pi variable'
print(pi)
inner()
输出:
inner pi variable
在运行上述程序时,内部函数的执行会打印其本地(LEGB 规则中的最高优先级)变量pi的值,因为它已在本地范围内定义并可用。
本地和全球范围:
如果变量未在本地范围内定义,则在更高范围内检查它,在本例中为全局范围。
# Global Scope
pi = 'global pi variable'
def inner():
pi = 'inner pi variable'
print(pi)
inner()
print(pi)
输出:
inner pi variable
global pi variable
因此,正如预期的那样,程序在执行inner()时打印出本地范围内的值。这是因为它是在函数内部定义的,这是查找变量的第一个位置。全局范围内的pi值在第 9 行执行print(pi)时打印。本地、封闭和全局范围:
对于封闭作用域,我们需要定义一个包含内部函数的外部函数,注释掉内部函数的局部pi变量,并使用nonlocal关键字引用pi 。
# Enclosed Scope
pi = 'global pi variable'
def outer():
pi = 'outer pi variable'
def inner():
# pi = 'inner pi variable'
nonlocal pi
print(pi)
inner()
outer()
print(pi)
输出:
outer pi variable
global pi variable
当external()被执行时, inner()和print函数都会被执行,这些函数会打印包含的pi变量的值。第 10 行的语句在 inner 的局部范围内查找变量,但在那里没有找到。由于pi是用nonlocal关键字引用的,这意味着pi需要从外部函数(即外部作用域)访问。总而言之,在本地范围内找不到pi变量,因此查找更高的范围。它在封闭和全局范围内都可以找到。但是根据 LEGB 层次结构,即使我们在全局范围中定义了一个,也会考虑封闭的范围变量。本地、封闭、全局和内置范围:
最终检查可以通过从数学模块导入pi并注释全局、封闭和局部pi变量来完成,如下所示:
# Built-in Scope
from math import pi
# pi = 'global pi variable'
def outer():
# pi = 'outer pi variable'
def inner():
# pi = 'inner pi variable'
print(pi)
inner()
outer()
输出:
3.141592653589793
由于pi没有在本地、封闭或全局范围内定义,因此查找内置范围,即从数学模块导入的pi值。由于程序能够在最外层范围内找到pi的值,因此得到以下输出,