Python 变量作用域
作用域决定了在程序的哪些地方可以访问和修改某个特定的变量。理解作用域可以防止意外的数据修改,并使你的代码逻辑更易于推理,尤其是在程序变得越来越复杂的时候。
在本章中,我们将探索 Python 中的局部作用域和全局作用域概念。
1. 局部变量 (Local Variables)
局部变量是指在函数内部定义的变量。它的作用域被严格限制在定义它的那个函数内部。这意味着,你只能在该函数内部访问和修改这个变量。一旦函数执行完毕,局部变量就会被销毁,它的值也就不再可访问了。
1.1 局部变量示例
def my_function():
# 'x' 是一个局部变量
x = 10
print("函数内部 x 的值为:", x)
my_function()
# 下面这行代码会导致错误,因为 'x' 在函数外部未定义
# print("函数外部 x 的值为:", x)在这个例子中,x 是 my_function 内部的一个局部变量。当你尝试在函数外部访问它时,Python 会抛出 NameError 报错,因为此时 x 已经超出了其作用域。
2. 嵌套函数中的作用域
如果你使用了嵌套函数(在一个函数内部定义了另一个函数),内层函数可以访问外层函数作用域中的变量,但是外层函数不能访问内层函数中定义的变量。
def outer_function():
# 'outer_var' 对于 outer_function 来说是局部变量
outer_var = 20
def inner_function():
# 'inner_var' 对于 inner_function 来说是局部变量
inner_var = 30
# inner_function 可以访问 outer_var
print("在 inner_function 内部访问 outer_var 的值:", outer_var)
print("在 inner_function 内部访问 inner_var 的值:", inner_var)
inner_function()
# 下面这行代码会导致错误,因为 'inner_var' 在 outer_function 的作用域中未定义
# print("在 inner_function 外部访问 inner_var 的值:", inner_var)
outer_function()在这种情况下,inner_function 可以访问 outer_var,因为它是在 outer_function 的封闭作用域(enclosing scope)内定义的。然而,outer_function 无法访问 inner_var,因为它仅仅定义在 inner_function 的局部作用域内。
3. 局部变量遮蔽 (Shadowing Local Variables)
如果一个局部变量与一个全局变量拥有相同的名字,那么在函数的作用域内,局部变量会**遮蔽(Shadow)**全局变量。这意味着在函数内部,程序会优先使用局部变量。
global_var = 5
def my_function():
# 全局变量 'global_var' 被同名的局部变量遮蔽了
global_var = 15
print("函数内部 global_var 的值为:", global_var)
my_function()
print("函数外部 global_var 的值为:", global_var)这里的输出结果将是:
函数内部 global_var 的值为: 15
函数外部 global_var 的值为: 5在 my_function 内部,global_var 指向的是那个局部变量,所以它的值是 15。而在函数外部,global_var 指向的仍然是全局变量,因此它的值保持为 5。
4. 全局变量 (Global Variables)
全局变量是在任何函数外部定义的变量。它的作用域是整个程序。这意味着你可以在代码的任何地方(包括函数内部)访问和修改它。
4.1 全局变量示例
# 'global_variable' 是一个全局变量
global_variable = 100
def my_function():
print("函数内部 global_variable 的值为:", global_variable)
my_function()
print("函数外部 global_variable 的值为:", global_variable)在这个例子中,global_variable 在 my_function 的内部和外部都是可以被访问的。
5. 在函数内部修改全局变量
虽然函数可以直接访问全局变量,但如果你想在函数内部修改它们,就必须使用 global 关键字。
如果不使用 global 关键字,当你尝试为同名变量赋值时,Python 会把它当作在函数作用域内创建了一个新的局部变量(正如我们在前面的“局部变量遮蔽”部分所看到的那样)。
global_variable = 200
def modify_global():
global global_variable # 声明我们想要使用的是全局变量
global_variable = 300
print("在 modify_global 内部 global_variable 的值为:", global_variable)
modify_global()
print("在 modify_global 外部 global_variable 的值为:", global_variable)输出结果为:
在 modify_global 内部 global_variable 的值为: 300
在 modify_global 外部 global_variable 的值为: 300通过使用 global 关键字,我们明确地告诉 Python:我们想要在函数内部修改那个名为 global_variable 的全局变量。
6. 何时使用全局变量
虽然全局变量有时很方便,但应该尽量少用。过度使用全局变量会导致代码难以理解、调试和维护。以下是一些适合使用全局变量的通用准则:
- 常量 (Constants):全局变量通常用于定义在整个程序中都不会改变的常量。
- 配置设置 (Configuration Settings):它们可用于存储程序不同部分都需要访问的全局配置项。
- 共享状态 (Shared State):在某些特定情况下,可能必须使用全局变量来在程序的不同模块间共享状态,但这需要非常谨慎地处理。
7. nonlocal 关键字
nonlocal 关键字用于嵌套函数中,用来引用**最近的封闭作用域(非全局)**中的变量。
当你想要在一个内层函数中修改外层函数作用域里的变量,但又不想在内层函数里创建一个新的同名局部变量时,这个关键字就派上用场了。
7.1 nonlocal 示例
def outer_function():
outer_var = 10
def inner_function():
nonlocal outer_var # 引用来自封闭作用域的 outer_var
outer_var = 20
print("在 inner_function 内部 outer_var 的值为:", outer_var)
inner_function()
print("在 outer_function 内部 outer_var 的值为:", outer_var)
outer_function()输出结果:
在 inner_function 内部 outer_var 的值为: 20
在 outer_function 内部 outer_var 的值为: 20如果不使用 nonlocal,inner_function 就会创建一个名为 outer_var 的新局部变量。通过使用 nonlocal,我们成功修改了属于 outer_function 作用域的 outer_var。
8. 实际案例与演示
让我们来看一些更复杂的例子,以巩固我们的理解。
8.1 案例 1:计算带折扣的总价
discount_rate = 0.1 # 用于折扣率的全局变量
def calculate_price(price, quantity):
"""计算应用折扣后的总价。"""
global discount_rate # 使用全局变量 (注:如果仅仅是读取而不修改,这行其实可以省略)
# 应用折扣
discount = price * discount_rate
final_price = (price - discount) * quantity
return final_price
# 示例用法
price = 100
quantity = 5
total_price = calculate_price(price, quantity)
print("总价为:", total_price) # 输出: 450.0在这个例子中,discount_rate 是一个全局变量。calculate_price 函数使用了这个全局变量来计算折扣。
8.2 案例 2:使用 nonlocal 实现计数器
def counter():
count = 0 # 这是一个封闭在 counter 中的变量
def increment():
nonlocal count
count += 1
return count
return increment
# 创建一个计数器实例
my_counter = counter()
# 多次调用 increment 函数
print(my_counter()) # 输出: 1
print(my_counter()) # 输出: 2
print(my_counter()) # 输出: 3在这里,counter 返回了 increment 函数本身(这在 Python 中被称为闭包 closure)。每次调用 my_counter()(实质上执行的是 increment 内的代码)时,包含在 counter 封闭作用域中的 count 变量都会被修改。如果没有 nonlocal,increment 每次运行都会创建一个新的 count 局部变量,那么这个计数器就无法正常累加了。