DAY 27 函数专题2:装饰器
ps:第一期day27对应5月16日
知识点回顾:
- 装饰器的思想:进一步复用
- 函数的装饰器写法
- 注意内部函数的返回值
作业:
编写一个装饰器 logger,在函数执行前后打印日志信息(如函数名、参数、返回值)
@logger
def multiply(a, b):return a * bmultiply(2, 3)
# 输出:
# 开始执行函数 multiply,参数: (2, 3), {}
# 函数 multiply 执行完毕,返回值: 6
本期内容如果无法理解,可以参考如下视频教学
B站-装饰器的使用
豆包:
以下是用生活场景比喻解释装饰器相关知识点,帮你秒懂这个「代码魔术」:
1. 装饰器的思想:给函数「穿外套」的复用艺术
本质:在不修改原函数代码的前提下,给函数「添加额外功能」,就像给衣服缝上口袋、给蛋糕加奶油。
核心目的:避免重复写相似代码(比如多个函数都需要「日志记录」功能),实现代码复用。比喻:
- 装饰器 = 礼物包装纸
- 原函数 = 「礼物本身」(比如一本书)。
- 装饰器 = 「包装纸 + 蝴蝶结」(给礼物增加「美观」功能)。
- 关键规则:不能拆开礼物修改内容(不修改原函数代码),但可以在外面套包装。
生活场景举例:
- 原函数
read_book()
是「看书」,装饰器可以是「记录看书时间」的功能:python
运行
def 记录时间(原函数): # 装饰器函数,参数是原函数def 包装函数():print("开始时间:2025-06-10 10:00")原函数() # 调用原函数(看书)print("结束时间:2025-06-10 11:00")return 包装函数@记录时间 # 用装饰器包装函数 def read_book():print("正在阅读《Python入门》")read_book() # 调用时会自动触发装饰器的「记录时间」功能
输出:plaintext
开始时间:2025-06-10 10:00 正在阅读《Python入门》 结束时间:2025-06-10 11:00
2. 函数装饰器的写法:三步搭出「包装流水线」
写法模板(以 Python 为例):
- 定义装饰器函数:接收原函数作为参数。
- 定义内部包装函数:在原函数前后添加新功能。
- 返回包装函数:用
@装饰器名
语法糖应用到目标函数。比喻拆解:包装礼物的流水线
python
运行
# 1. 定义「包装纸」函数(装饰器) def 包装纸(礼物): # 礼物=原函数# 2. 定义「包装过程」函数(内部函数)def 包礼物():print("✨ 放上丝带") # 新增功能1礼物() # 调用原礼物(不修改原内容)print("🎀 贴上祝福卡片") # 新增功能2# 3. 返回「包装好的礼物」return 包礼物# 用装饰器包装「送书」函数 @包装纸 # 等价于 send_book = 包装纸(send_book) def send_book():print("📚 送出《Python书》")# 调用包装后的函数 send_book()
执行效果:
plaintext
✨ 放上丝带 📚 送出《Python书》 🎀 贴上祝福卡片
3. 注意内部函数的返回值:别让礼物「消失在包装里」
常见错误:如果内部函数不返回原函数的结果,原函数的返回值会丢失(就像把礼物放进盒子却不装进去,最后送出空盒子)。
比喻对比:
- 正确做法:包装礼物时必须把「礼物本身」放进盒子,否则收礼人拿到的是空盒子。
- 错误做法:包装时只放包装纸,没放礼物,结果丢失了礼物。
代码示例:
python
运行
# ❌ 错误写法:内部函数不返回原函数结果 def 错误装饰器(原函数):def 包装函数():print("开始装饰")原函数() # 调用原函数,但没保存结果print("装饰结束")return 包装函数@错误装饰器 def get_gift():return "钻石戒指" # 原函数返回一个值result = get_gift() print(result) # 输出:None(原函数的返回值丢失了!)# ✅ 正确写法:必须返回原函数的调用结果 def 正确装饰器(原函数):def 包装函数():print("开始装饰")gift = 原函数() # 保存原函数的返回值print("装饰结束")return gift # 返回原函数的结果return 包装函数@正确装饰器 def get_gift():return "钻石戒指"result = get_gift() print(result) # 输出:钻石戒指(结果正确传递)
终极比喻总结:装饰器是「函数的变装皇后」
装饰器要素 生活比喻 核心作用 原函数 素颜的人 原本的功能主体 装饰器函数 造型师 负责给函数「化妆」(添加功能) 内部包装函数 化妆过程 在原功能前后插入新操作 @装饰器语法 造型师的「魔法咒语」 一键应用变装效果 必须返回原结果 化妆不能改变人的本质 装饰后必须保留原函数的返回值 通过「包装礼物」的比喻,可以直观理解装饰器的「无侵入式增强」特性 —— 就像我们不会为了系蝴蝶结而拆开礼物盒,装饰器也不会修改原函数的代码,却能让函数「穿上新功能的外衣」,实现高效复用~ 🎁
另回答:
好的!我会用生活中的例子来比喻这三个知识点,尽量让抽象的概念变得直观~
1. 装饰器的思想:进一步复用
知识点解释:
装饰器是一种不修改原有函数代码,却能为其动态添加新功能的编程技巧。它的核心是 “复用代码逻辑”,比如给多个函数统一添加日志记录、权限校验等功能,避免重复写重复代码。
比喻:
就像给蛋糕加装饰:
- 蛋糕本身(原有函数)的配方(代码)不需要改变,
- 但可以通过裱花(装饰器)给不同蛋糕(不同函数)加上奶油、水果(新功能),
- 而且同一套裱花工具(装饰器逻辑)可以重复用在多个蛋糕上,避免每次都重新设计装饰。
场景举例:
比如多个函数都需要记录执行时间,用装饰器就像给每个函数 “贴” 一个计时器,不用每个函数里都写一遍计时代码。
2. 函数的装饰器写法
知识点解释:
装饰器本质是一个高阶函数(参数或返回值是函数的函数),写法分两步:
- 定义装饰器函数:它接受一个函数(被装饰的函数)作为参数,返回一个内部函数(包裹了原函数的新功能)。
- 用
@装饰器名
语法糖给目标函数 “装饰”。
比喻:
就像做三明治:
- 面包片 1(装饰器外层函数):接受食材(原函数)作为参数,
- 夹心菜肉(内部函数):在食材前后添加面包片(新功能),比如先抹酱(前置逻辑),再放食材(原函数执行),最后盖另一片面包(后置逻辑),
- 成品三明治(返回的内部函数):比原来的食材更丰富,但保留了食材本身的味道(原函数功能)。
代码示例:
python
# 定义装饰器:给函数加“吃前洗手”和“吃完擦嘴”的逻辑
def sandwich_decorator(func):def wrapper(): # 内部函数,包裹原函数print("✨ 先洗手(前置逻辑)")func() # 执行原函数(吃三明治)print("✨ 再擦嘴(后置逻辑)")return wrapper # 返回包裹后的函数# 用@语法糖装饰目标函数
@sandwich_decorator
def eat_sandwich():print("🍔 正在吃三明治(原函数逻辑)")# 调用装饰后的函数
eat_sandwich()
输出:
plaintext
✨ 先洗手(前置逻辑)
🍔 正在吃三明治(原函数逻辑)
✨ 再擦嘴(后置逻辑)
3. 注意内部函数的返回值
知识点解释:
如果原函数有返回值(比如计算结果),装饰器的内部函数必须显式返回原函数的调用结果,否则调用装饰后的函数会得到None
(空值)。
比喻:
就像送快递:
- 原函数是 “打包商品” 的函数,返回值是 “包裹里的商品”,
- 装饰器是 “贴快递单” 的流程,如果贴单后不把包裹(返回值)交给快递员(调用者),买家收到的就是空包裹(
None
)。
代码示例:
python
# 错误示范:内部函数不返回原函数结果
def bad_decorator(func):def wrapper():func() # 执行原函数,但不返回结果return wrapper@bad_decorator
def add(a, b):return a + b # 原函数有返回值result = add(1, 2)
print(result) # 输出:None(丢失了返回值!)
python
# 正确示范:内部函数返回原函数结果
def good_decorator(func):def wrapper(*args, **kwargs): # 通用参数接收return func(*args, **kwargs) # 必须返回原函数调用结果return wrapper@good_decorator
def add(a, b):return a + bresult = add(1, 2)
print(result) # 输出:3(正确保留返回值)
总结
- 装饰器思想:像给函数 “穿外套”,复用功能代码,不修改原函数。
- 写法关键:装饰器是高阶函数,用
@
语法糖简化调用,内部函数包裹原函数。 - 返回值注意:内部函数必须 “接住” 原函数的返回值并返回,否则结果会 “丢失”。
如果还有疑问,可以随时问我哦! 😊
代码:
def logger(func):def wrapper(*args, **kwargs):print(f"调用函数: {func.__name__},参数: {args}, {kwargs}")result = func(*args, **kwargs)print(f"函数返回值: {result}")return resultreturn wrapper
@logger
def multiply(a, b):return a * b multiply(2, 3)
另代码:
def logger(func):def wrapper(*args, **kwargs):# 打印函数执行前的日志print(f"开始执行函数: {func.__name__}")print(f"位置参数: {args}")print(f"关键字参数: {kwargs}")# 执行原函数result = func(*args, **kwargs)# 打印函数执行后的日志print(f"函数 {func.__name__} 执行完毕")print(f"返回值: {result}")return resultreturn wrapper# 示例函数,用于测试装饰器
@logger
def add_numbers(a, b):return a + b# 调用示例函数
add_numbers(3, 5)