一、什么是装饰
想象你买了一块普通的面包。
装饰器做的事情就是这样:
在不改变面包(原函数)本身的前提下,给它“涂上”额外的功能(奶油、巧克力等)。
Python 里的装饰器,就是让你能写出这样的代码:
@涂奶油@撒巧克力def 面包(): print("我是普通面包")
调用 面包() 的时候,实际执行的是 先涂奶油、再撒巧克力、最后再执行原本的面包。
二、Python 装饰器
概念:
装饰器是一个函数(也可以是一个类),它接收一个函数作为参数,然后返回一个新的函数。
新函数里会包含原函数的功能,再加上一些额外的“装饰”。
语法:
def my_decorator(func): def wrapper(): print("在调用原函数之前做点啥") # 前置装饰 func() # 调用原函数 print("在调用原函数之后做点啥") # 后置装饰 return wrapper@my_decoratordef say_hello(): print("Hello!")say_hello()
运行输出:
ounter(lineounter(lineounter(line在调用原函数之前做点啥Hello!在调用原函数之后做点啥
你看,say_hello 本身只负责打印 "Hello!",但通过 @my_decorator,它自动拥有了“前后加点料”的能力。
三、趣味代码样例
1. 计时装饰器(给函数计时)
import timedef timer(func): def wrapper(): start = time.time() func() end = time.time() print(f"{func.__name__} 耗时 {end - start:.4f} 秒") return wrapper@timerdef slow_job(): time.sleep(1) print("任务完成")slow_job()# 输出:# 任务完成# slow_job 耗时 1.0003 秒
2. 重复执行装饰器(让一个函数自动执行 3 次)
def repeat(times): def decorator(func): def wrapper(): for i in range(times): print(f"第 {i+1} 次执行") func() return wrapper return decorator@repeat(3)def dance(): print("跳舞")dance()
这里用了“带参数的装饰器”,本质是在外面再套一层函数来接收参数。
3. 多个装饰器叠加
def wear_hat(func): def wrapper(): print("👒 戴上帽子") func() return wrapperdef wear_glasses(func): def wrapper(): print("👓 戴上眼镜") func() return wrapper@wear_hat@wear_glassesdef me(): print("😀 这才是我")me()# 输出:# 👒 戴上帽子# 👓 戴上眼镜# 😀 这才是我
注意:装饰的顺序是从下往上(离函数最近的先装饰),但执行时是从上往下。
理解成“先贴最里面的衣服,再穿外面的外套”。
四、Java 和 C++ —— 有“装饰器”吗?
Java
有类似的设计模式叫“装饰器模式”,但没有 Python 这种 @ 语法糖。
Java 装饰器模式示例(比如给咖啡加糖加牛奶):
interface Coffee { String getCost(); }class SimpleCoffee implements Coffee { public String getCost() { return "5元"; }}class SugarDecorator implements Coffee { private Coffee coffee; public SugarDecorator(Coffee c) { this.coffee = c; } public String getCost() { return coffee.getCost() + " + 糖 1元"; }}// 使用时:Coffee c = new SugarDecorator(new SimpleCoffee());c.getCost(); // "5元 + 糖 1元"
与 Python 异同:
- Python 装饰器是运行时对函数/类的直接替换,写法极其简洁。
- Java 需要手动创建包装类,代码更啰嗦,但类型更安全(编译时)。
- Java 的注解(Annotation) 长得像装饰器(如
@Override),但注解只是元数据,不改变行为,需要配合反射或注解处理器才有效,和 Python 装饰器不是一回事。
C++
没有内置的装饰器语法。但可以通过函数包装、lambda、std::function、继承等模拟类似效果。
C++ 模拟装饰器示例(用 lambda 包装):
#include<iostream>#include<functional>auto timer = [](std::function<void()> func) { return [func]() { std::cout << "开始计时" << std::endl; func(); std::cout << "结束计时" << std::endl; };};intmain(){ auto sayHi = []() { std::cout << "Hi!" << std::endl; }; auto decorated = timer(sayHi); decorated();}
与 Python 异同:
- Python 有
@ 语法糖,写起来像声明式;C++ 必须显式调用包装函数,代码可读性较差。 - C++ 模板元编程可以实现编译期的“装饰”,但极其复杂,日常开发很少这样用。
- C++ 更推荐使用继承 + 组合来替代装饰器需求。
五、总结:Python 装饰器的魅力
| | | |
|---|
| | | |
| | | |
| | 用 lambda / std::function 手动包装 | |
Python 装饰器的优势:
- 极简:一行
@xxx 就能为函数增加缓存、日志、权限校验、计时等通用功能。
初学小贴士:
- 装饰器里的
wrapper 函数记得要 return 出来。 - 如果原函数有参数,
wrapper 要写成 *args, **kwargs 通用形式。 - 想保留原函数的元信息(如名字、文档),可以用
functools.wraps(func)。