

🎯 说明:尽管《【Python】基础语法入门》系列原计划九篇完结,但许多读者反馈希望深入理解函数的高级特性。因此,本篇作为特别加更,聚焦 Python 函数的三大进阶概念:作用域(Scope)、闭包(Closure)和装饰器(Decorator)。这些内容虽属“中级”,却是写出优雅、灵活代码的关键!
你将学会:
- 为什么变量有时“找不到”?——理解 LEGB 规则
- 如何让函数“记住”外部状态?——闭包的妙用
- 如何不修改原函数就增强功能?——装饰器实战
即使初次接触这些概念,本文也会用生活化例子 + 清晰代码带你轻松掌握。
1. 变量作用域(Scope)与 LEGB 规则
Python 中,变量并不是在任何地方都能被访问。它的可见范围由作用域决定。
四层作用域(LEGB):
| 层级 | 含义 | 示例 |
|---|---|---|
| Local | 函数内部 | def f(): x = 1 |
| Enclosing | 外层函数(嵌套函数) | def outer(): x=1; def inner(): print(x) |
| Global | 模块顶层 | 文件最外层的 x = 10 |
| Built-in | 内置名称 | len, print, range |
Python 按 L → E → G → B 顺序查找变量。
示例 1:全局 vs 局部
x = "全局变量"
def func():
x = "局部变量"
print("函数内:", x) # 局部变量
func()
print("函数外:", x) # 全局变量
✅ 局部变量不会影响全局变量。
示例 2:修改全局变量(global)
count = 0
def increment():
global count # 声明使用全局变量
count += 1
increment()
print(count) # 1
⚠️ 少用
global!它会降低代码可读性和可测试性。
示例 3:嵌套函数与 nonlocal
def outer():
x = "外层变量"
def inner():
nonlocal x # 声明使用外层(非全局)变量
x = "被 inner 修改"
print("inner 中:", x)
inner()
print("outer 中:", x)
outer()
# 输出:
# inner 中: 被 inner 修改
# outer 中: 被 inner 修改
🔑
nonlocal用于在嵌套函数中修改外层函数的局部变量。
2. 闭包(Closure):函数的“记忆”能力
闭包是指:一个内部函数引用了外部函数的变量,即使外部函数已执行完毕,内部函数仍能访问这些变量。
生活比喻:
就像你租了一个带保险箱的房间。退房后,别人不能进房间,但你保留了保险箱钥匙,仍能取出里面的东西。
代码示例:
def make_multiplier(n):
"""返回一个乘法函数,该函数会记住 n"""
def multiplier(x):
return x * n # 引用了外层变量 n
return multiplier
# 创建两个闭包
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15
# 查看闭包保存的变量
print(double.__closure__[0].cell_contents) # 2
✅ 闭包的三个条件:
- 存在嵌套函数
- 内层函数引用了外层函数的变量
- 外层函数返回了内层函数
实战用途:
- 配置化函数:如日志级别、API 基地址
- 回调函数携带状态
- 实现简单的工厂模式
3. 装饰器(Decorator):不侵入地增强函数
装饰器是 Python 最优雅的语法糖之一。它允许你在不修改原函数代码的前提下,为其添加新功能(如日志、计时、权限校验等)。
核心思想:
装饰器 = 接收一个函数 → 返回一个增强版函数
步骤 1:手动实现装饰逻辑
def say_hello():
print("Hello!")
# 想给 say_hello 加上“执行时间”打印
def log_time(func):
def wrapper():
import time
start = time.time()
func() # 调用原函数
end = time.time()
print(f"函数 {func.__name__} 耗时: {end - start:.4f} 秒")
return wrapper
# 手动装饰
say_hello = log_time(say_hello)
say_hello()
步骤 2:使用 @ 语法糖(推荐)
import time
def log_time(func):
def wrapper(*args, **kwargs): # 支持任意参数
start = time.time()
result = func(*args, **kwargs) # 获取返回值
end = time.time()
print(f"⏱️ {func.__name__} 耗时: {end - start:.4f}s")
return result # 返回原函数结果
return wrapper
@log_time
def greet(name):
time.sleep(0.1) # 模拟耗时操作
return f"你好, {name}!"
msg = greet("小明")
print(msg)
# 输出:
# ⏱️ greet 耗时: 0.1002s
# 你好, 小明!
✅ 关键点:
- 使用
*args, **kwargs让装饰器通用- 别忘了
return result,否则原函数返回值会丢失!
进阶:带参数的装饰器
有时我们希望装饰器本身也能接收参数,例如指定日志级别:
def log(level="INFO"):
def decorator(func):
def wrapper(*args, **kwargs):
print(f"[{level}] 调用函数: {func.__name__}")
return func(*args, **kwargs)
return wrapper
return decorator
@log(level="DEBUG")
def connect_db():
print("连接数据库...")
connect_db()
# 输出:
# [DEBUG] 调用函数: connect_db
# 连接数据库...
结构:三层函数嵌套
- 最外层:接收装饰器参数
- 中间层:接收被装饰函数
- 最内层:替换原函数的逻辑
4. 常见内置装饰器
Python 自带几个实用装饰器:
@staticmethod / @classmethod
(已在面向对象篇介绍)
@property:将方法变为属性
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def area(self):
return 3.14159 * self._radius ** 2
c = Circle(5)
print(c.area) # 78.53975 (像访问属性一样调用方法)
5. 总结与最佳实践
| 概念 | 用途 | 注意事项 |
|---|---|---|
| 作用域(LEGB) | 理解变量查找规则 | 避免滥用 global |
| 闭包 | 让函数携带状态 | 注意变量绑定问题(循环中创建闭包需谨慎) |
| 装饰器 | 无侵入式增强函数 | 保留原函数元信息(可用 functools.wraps) |
补充:保留函数元信息
装饰后,原函数的 __name__、__doc__ 会变成 wrapper,可用 @functools.wraps 修复:
from functools import wraps
def my_decorator(func):
@wraps(func) # ← 关键!
def wrapper(*args, **kwargs):
"""这是 wrapper 的 docstring"""
return func(*args, **kwargs)
return wrapper
@my_decorator
def example():
"""这是 example 的说明"""
pass
print(example.__name__) # example(不是 wrapper)
print(example.__doc__) # 这是 example 的说明
下一步建议
- 尝试写一个 缓存装饰器(用字典缓存函数结果)
- 用闭包实现一个 计数器生成器
- 在你的 To-Do List 项目中,为关键函数加上 日志装饰器
💡 装饰器和闭包是 Python 高阶编程的基石。掌握它们,你就离“Pythonic”代码更近了一步!
继续探索,代码世界因你而精彩! 🐍


—— 函数进阶:作用域、闭包与装饰器&spm=1001.2101.3001.5002&articleId=155443094&d=1&t=3&u=71e41cc8679546acb3e5359ffd014cfa)
3536

被折叠的 条评论
为什么被折叠?



