1. 基础篇:什么是局部和全局变量?
想象你在厨房做饭,ingredient
(食材)是全局的,因为整个厨房都能用到它。而当你在切洋葱时,那把刀(knife
)就是局部的,只在这个特定任务(函数)里使用。
ingredient = "洋葱"
def chop():
knife = "锋利的菜刀"
print(f"用{knife}切{ingredient}")
chop()
这里,knife
仅在chop
函数内部可见,就是局部变量,而ingredient
是全局变量,哪里都能访问。
2. 修改全局变量的第一坑:你以为你能改?
直接在函数里修改全局变量?Python可不轻易让你得逞!
global_var = 10
def change_global():
global_var = 20 # 注意,这只是创建了一个新的局部变量!
change_global()
print(global_var) # 猜猜看,输出是多少?
输出还是10!Python说:“嘿,你这是新建了个局部的global_var
,原来的我可没动哦。”
3. 正确修改全局变量:要用global
关键字!
想动我的全局变量?得先打招呼!
global_var = 10
def change_global_correctly():
global global_var
global_var = 20
change_global_correctly()
print(global_var) # 这次对了吧?
这次,输出是20,因为我们明确告诉Python:“嘿,我要动的是全局的那个家伙。”
4. 局部变量的“幽灵”效应
当你在函数内未声明就使用变量名,Python会认为你在找全局变量,但这可能会引发一些诡异的现象。
def mystery():
print(unknown_var) # 啊哦,这是谁?
try:
mystery()
except NameError as e:
print(e) # 未知变量错误,它真的存在吗?
这会抛出NameError,提醒你“unknown_var”这个幽灵并不存在于全局空间。
5. 非直观的变量作用域:嵌套函数
嵌套函数可以访问外层函数的变量,但修改时要小心!
def outer():
outer_var = "外层的宝藏"
def inner():
print(outer_var) # 能找到我外公的宝藏吗?
outer_var = "被内层修改了" # 实际上,这创造了一个新的局部变量
inner()
print(outer_var) # 外层的值会变吗?
outer() # 来看看结果
你会发现,外层的值没变,因为内层创建了一个同名的局部变量。
6. 使用nonlocal
关键字的场景
当你确实想在嵌套函数中修改外层函数的变量时,nonlocal
来帮忙!
def outer():
outer_var = "原始宝藏"
def inner():
nonlocal outer_var
outer_var = "宝藏升级了"
print(outer_var)
inner()
print(outer_var) # 这次会怎样?
outer() # 哈哈,成功修改!
nonlocal
关键字让Python知道你想修改的是外层的变量,不是创建新的。
7. 小心闭包的陷阱
闭包是Python中的高级特性,但也可能因变量作用域而让人困惑。
def create_counter():
count = 0
def counter():
nonlocal count
count += 1
return count
return counter
my_counter = create_counter()
print(my_counter()) # 1
print(my_counter()) # 2
# 看,count被正确地保留和增加了!
闭包可以记住外部函数的状态,但记得,这也意味着它可能会保留比预期更多的内存,所以使用时要谨慎。
8. 利用模块级别的变量
在大型项目中,有时需要在整个模块范围内共享数据。你可以定义模块级别的变量来实现这一目的。但请记住,这样做可能会增加模块间的耦合度,要谨慎使用。
# my_module.py
shared_data = []
def add_to_shared(data):
shared_data.append(data)
def get_shared():
return shared_data
# 另一个文件中使用
import my_module
my_module.add_to_shared("Hello")
print(my_module.get_shared()) # 输出: ['Hello']
9. 全局变量的替代方案:配置文件与环境变量
在处理配置信息或应用设置时,使用配置文件(如.ini
, .json
, 或环境变量)是一个更好的选择,而不是硬编码全局变量。这样可以提高代码的灵活性和可维护性。
# 假设有一个config.json
{
"database": "my_db",
"port": 5432
}
import json
import os
# 读取配置文件
with open('config.json') as f:
config = json.load(f)
# 或者使用环境变量
DB_NAME = os.getenv('DB_NAME', 'default_db') # 如果环境变量不存在,则使用'default_db'
10. 上下文管理器与with
语句
虽然这不是直接关于变量作用域的,但了解上下文管理器可以帮助你更好地管理资源,比如文件操作时的自动关闭。
with open('myfile.txt', 'w') as file:
file.write("Hello, world!")
# 文件在这里自动关闭,无需显式调用file.close()
这里的file
变量在with
块内有效,一旦执行完毕,Python会确保资源得到释放。
11. 闭包的高级应用:记忆化
记忆化是一种优化技术,用于存储函数计算的中间结果,减少重复计算。这对于有重叠子问题的递归函数尤其有用。
def memoize(func):
cache = {}
def wrapper(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return wrapper
@memoize
def fibonacci(n):
if n <= 1:
return n
else:
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10)) # 55,而且只计算了一次n=1到n=10
通过这种方式,memoize
装饰器创建了一个闭包,它记录了函数调用的结果,避免了重复劳动。
附加:下面举例全局变量与局部变量
####全局变量变量名大写
####局部变量变量名小写
# 如果函数的内容无global关键字,优先读取局部变量,能读取全局变量,无法对全局变量重新赋值 NAME='fff'
# 但是对于可变类型,可以对内部元素进行操作
# 如果函数中有global关键字,变量本质上就是全局的哪个变量,可读取可赋值, NAME='fff'
NAME = ["哈哈","拉拉"]
def test():
global NAME
NAME.append("添加")
print("你好",NAME)
test()
#错误示例
# 以下这个不知道找哪个变量了
NAME = ["哈哈","拉拉"]
def test1():
NAME.append("添加")
global NAME
print("你好",NAME)
test1()