作用域(scope)是指变量的可见范围,决定了变量在程序的哪些部分可以被访问。Python 中主要有四种作用域:
- 局部(Local)作用域
- 封闭(Enclosing)作用域
- 全局(Global)作用域
- 内置(Built-in)作用域
在作用域中按名称去寻找对象(Python 中一切皆对象)时,会按照 LEGB
规则去查找。如果发生重名,也会按照 LEGB
规则,谁先被找到就用谁。
所谓 LEGB
规则,很简单,就是作用域查找顺序为:Local -> Enclosing -> Global -> Built-in
。
另外需要注意的是 Python 里是没有块级作用域(Block scope)的。
for i in range(10): pass print(i)
以上代码会打印出 9。如果有块级作用域,那么在 for
块结束后,就不应该能访问 i
,而现在,会得到 i
的最后一个值。更糟糕的是,如果 range()
里是 0,那么 for
循环会直接退出,这时再 print(i)
,结果就是:
NameError: name ‘i’ is not defined
所以,虽然 Python 没有块级作用域,但是建议就当它有,不要在代码块以外,使用代码块内定义的东西,以避免混乱。
局部作用域(Local Scope)
局部作用域指函数内部的变量,仅在函数内部可见,函数外部无法访问,比如下面的示例:
def greet(): name = "Alice" # 局部变量 print(f"Hello, {name}") greet() print(name) # 出错,因为 name 在函数外不可见
在上面代码中 name
变量是在 greet
函数内部定义的,只能在函数内部访问,函数外部无法访问。
另外需要注意 Python 中不在局部作用域里的变量默认是只读的,如果试图围为其绑定一个新的值,Python 会认为是在当前的局部作用域里创建一个新的变量,而不是修改外部的变量。
x = 10 def func(): x = 20 # 创建了一个新的局部变量 x print(x) func() print(x) # 输出: 10
上面的这段代码中,func
函数内部的 x
是一个新的局部变量,不会影响外部的 x
变量。如果我们直接在函数内部修改 x
的值,会报错:
x = 10 def func(): # UnboundLocalError: cannot access local variable 'x' where it is not associated with a value x += 20 # 报错,因为 x 是只读的 print(x) func()
上面代码中我们在 func
函数内部修改 x
的值,会报错,因为在 func
函数内部的 x
现在相当于是一个局部变量,是只读的,无法修改。
封闭作用域(Enclosing Scope)
封闭作用域指嵌套函数中的外层函数作用域,内部函数可以访问外层函数的变量。
def outer(): x = "外层变量" def inner(): print(x) # 可以访问外层变量,x 就是封闭作用域 inner() outer()
inner
函数可以访问 outer
函数中的 x
变量。
全局作用域(Global Scope)
全局作用域指模块或脚本级别的作用域,整个模块或脚本中都可以访问全局变量。
x = "全局变量" def func(): print(x) # 访问全局变量 func() print(x) # 全局变量也可以在函数外部访问
x
是全局变量,可以在函数内外访问。
内置作用域(Built-in Scope)
内置作用域指 Python 提供的内置函数和变量(如 print
,len
等)。
# 使用内置函数 len numbers = [1, 2, 3] print(len(numbers)) # 输出: 3
len
是 Python 的内置函数,所以可以在任何地方直接使用。
global
关键字
global
关键字用来声明一个全局变量。在局部作用域想要对全局作用域的全局变量(不可变类型)进行修改时,需要使用 global
关键字。当然对于可变数据类型(list、dict 等)可以直接引用而不用通过 global
关键字。
x = "全局变量" def func(): global x x = "修改后的全局变量" func() print(x) # 输出: 修改后的全局变量
使用 global
关键字在函数内部修改了全局变量 x
。
nonlocal
关键字
除了 global
关键字,Python 还提供了 nonlocal
关键字,用于在嵌套函数中修改封闭作用域中的变量。
def outer(): x = "外层变量" def inner(): nonlocal x x = "修改后的外层变量" inner() print(x) # 输出: 修改后的外层变量 outer()
这里我们使用 nonlocal
关键字在 inner
函数中修改了 outer
函数中的 x
变量。
了解作用域可以帮助我们避免变量名冲突,合理管理变量,希望大家能够通过这些例子,更好的理解作用域的概念和用法。