Python34_装饰器知识
文章目录
- Python34_装饰器知识
- @[toc]
- 1-核心知识
- 2-装饰器代码示例
- 3-辨识函数是否装饰器
- 一、从代码结构辨识(最核心)
- 1. 高阶函数特征
- 2. 返回对象必须是"可调用"的
- 二、从使用方式辨识
- 1. `@` 语法糖(显性标识)
- 2. 手动调用形式(等价写法)
- 三、从行为模式辨识
- 1. "透明增强"原则
- 2. 是否包装(Wrap)原函数
- 四、实用辨识技巧
- 技巧 1:用 `inspect` 检查
- 技巧 2:运行时验证
- 五、常见混淆情况辨析
- 六、一句话总结
- 4-Python装饰器(Decorator)学习笔记
- 📚 核心概念
- 简单理解
- 🎯 装饰器的5个层次
- 1️⃣ 基础装饰器
- 2️⃣ 保留元信息 (使用@wraps)
- 3️⃣ 带参数的装饰器(装饰器工厂)
- 4️⃣ 参数验证装饰器(类似你的需求)
- 5️⃣ 实用装饰器示例
- 🔑 核心要点总结
- 🚀 运行示例
文章目录
- Python34_装饰器知识
- @[toc]
- 1-核心知识
- 2-装饰器代码示例
- 3-辨识函数是否装饰器
- 一、从代码结构辨识(最核心)
- 1. 高阶函数特征
- 2. 返回对象必须是"可调用"的
- 二、从使用方式辨识
- 1. `@` 语法糖(显性标识)
- 2. 手动调用形式(等价写法)
- 三、从行为模式辨识
- 1. "透明增强"原则
- 2. 是否包装(Wrap)原函数
- 四、实用辨识技巧
- 技巧 1:用 `inspect` 检查
- 技巧 2:运行时验证
- 五、常见混淆情况辨析
- 六、一句话总结
- 4-Python装饰器(Decorator)学习笔记
- 📚 核心概念
- 简单理解
- 🎯 装饰器的5个层次
- 1️⃣ 基础装饰器
- 2️⃣ 保留元信息 (使用@wraps)
- 3️⃣ 带参数的装饰器(装饰器工厂)
- 4️⃣ 参数验证装饰器(类似你的需求)
- 5️⃣ 实用装饰器示例
- 🔑 核心要点总结
- 🚀 运行示例
1-核心知识
- 装饰器代码代码示例 -> 入参和返回值都【函数】
- 如何辨识一个函数是装饰器? -> 接收【函数】,返回也必须是【函数】
- 装饰器的3个层次 -> 基础装饰器+保留元信息+带参数装饰器
2-装饰器代码示例
""" Python装饰器(Decorator)核心概念学习 装饰器本质:是一个接受函数作为参数,并返回一个新函数的函数 """# ==================== 第1层:最基础的装饰器 ====================defmy_decorator(func):"""装饰器本质:包装原函数,添加额外功能"""defwrapper():print("🔵 装饰器:函数调用前")result=func()# 调用原函数print("🔵 装饰器:函数调用后")returnresultreturnwrapper@my_decoratordefsay_hello():print(" Hello World!")# ==================== 第2层:装饰器保留原函数的元信息 ====================fromfunctoolsimportwrapsdefbetter_decorator(func):"""使用wraps保留原函数的名称、文档字符串等信息"""@wraps(func)defwrapper(*args,**kwargs):print(f"🟢 调用函数:{func.__name__}")returnfunc(*args,**kwargs)returnwrapper@better_decoratordefgreet(name):"""向某人打招呼"""returnf"Hello,{name}!"# ==================== 第3层:带参数的装饰器(装饰器工厂) ====================defrepeat(times):""" 带参数的装饰器工厂 返回一个真正的装饰器 """defdecorator(func):@wraps(func)defwrapper(*args,**kwargs):results=[]foriinrange(times):print(f"🔄 第{i+1}次调用")result=func(*args,**kwargs)results.append(result)returnresultsreturnwrapperreturndecorator@repeat(times=3)defsay_hi(name):returnf"Hi,{name}!"# ==================== 第4层:类似你提到的参数验证装饰器 ====================deftool_parameters(parameters):""" 类似TypeScript中 @tool_parameters 的Python实现 验证函数参数的类型和必需性 """defdecorator(func):@wraps(func)defwrapper(**kwargs):# 获取参数定义properties=parameters.get("properties",{})required=parameters.get("required",[])# 验证必需参数forparaminrequired:ifparamnotinkwargs:raiseValueError(f"缺少必需参数: '{param}'")# 验证参数类型forparam_name,param_valueinkwargs.items():ifparam_nameinproperties:expected_type=properties[param_name].get("type")actual_type=type(param_value).__name__# 类型映射type_mapping={"str":str,"int":int,"float":float,"bool":bool,"list":list,"dict":dict}ifexpected_typeintype_mapping:expected_python_type=type_mapping[expected_type]ifnotisinstance(param_value,expected_python_type):raiseTypeError(f"参数 '{param_name}' 类型错误: "f"期望{expected_type}, 实际{actual_type}")# 验证通过,调用原函数returnfunc(**kwargs)returnwrapperreturndecorator# 使用示例@tool_parameters({"type":"object","properties":{"path":{"type":"str"},"count":{"type":"int"}},"required":["path"],})defread_file(path,count=1):"""模拟读取文件"""returnf"读取文件:{path}, 次数:{count}"@tool_parameters({"type":"object","properties":{"user_id":{"type":"int"},"username":{"type":"str"},"active":{"type":"bool"}},"required":["user_id","username"],})defcreate_user(user_id,username,active=True):"""模拟创建用户"""return{"id":user_id,"name":username,"active":active}# ==================== 第5层:带返回值的装饰器 ====================defmeasure_time(func):"""测量函数执行时间"""importtimeimporttimeastime_module@wraps(func)defwrapper(*args,**kwargs):start=time_module.time()result=func(*args,**kwargs)end=time_module.time()print(f"⏱️{func.__name__}执行时间:{end-start:.4f}秒")returnresultreturnwrapper@measure_timedefslow_function():"""模拟耗时操作"""importtime time.sleep(0.1)return"完成"# ==================== 运行所有示例 ====================defmain():print("="*60)print("📚 Python装饰器核心概念学习")print("="*60)print("\n1️⃣ 基础装饰器:")say_hello()print("\n2️⃣ 保留元信息的装饰器:")print(f" 结果:{greet('Alice')}")print(f" 函数名:{greet.__name__}")print(f" 文档:{greet.__doc__}")print("\n3️⃣ 带参数的装饰器(重复3次):")results=say_hi("Bob")print(f" 结果:{results}")print("\n4️⃣ 参数验证装饰器:")# 正确调用try:result=read_file(path="/path/to/file",count=2)print(f" ✅ 正确调用:{result}")except(ValueError,TypeError)ase:print(f" ❌ 错误:{e}")# 缺少必需参数try:result=read_file(count=2)print(f" 结果:{result}")except(ValueError,TypeError)ase:print(f" ❌ 缺少必需参数:{e}")# 类型错误try:result=read_file(path=123)# path应该是字符串print(f" 结果:{result}")except(ValueError,TypeError)ase:print(f" ❌ 类型错误:{e}")print("\n5️⃣ 创建用户(参数验证):")try:user=create_user(user_id=1,username="Tom",active=False)print(f" ✅ 创建成功:{user}")except(ValueError,TypeError)ase:print(f" ❌ 错误:{e}")print("\n6️⃣ 测量执行时间:")result=slow_function()print(f" 结果:{result}")print("\n"+"="*60)print("🎉 学习完成!")print("="*60)if__name__=="__main__":main()3-辨识函数是否装饰器
辨识一个函数是否是装饰器,可以从代码结构特征、使用方式、行为模式三个维度来判断。以下是系统化的辨识方法:
一、从代码结构辨识(最核心)
装饰器在代码层面有明确的结构指纹:
1. 高阶函数特征
装饰器一定是高阶函数(Higher-Order Function),即:
- 接收函数作为参数
- 返回函数(或可调用对象)
# 典型的装饰器结构def装饰器名(func):# ← 参数是一个函数defwrapper(*args,**kwargs):# 增强逻辑...returnfunc(*args,**kwargs)# ← 调用并返回原函数结果returnwrapper# ← 返回一个函数反例(不是装饰器):
# 虽然接收函数,但返回的不是函数defbad_example(func):return42# 返回 int,不是装饰器# 虽然返回函数,但不接收函数参数defnot_decorator():definner():passreturninner# 工厂函数,不是装饰器2. 返回对象必须是"可调用"的
装饰器返回的必须是可调用对象(Callable),常见类型:
| 返回类型 | 示例 | 是否算装饰器 |
|---|---|---|
| 函数 | return wrapper | ✅ 是 |
类的实例(含__call__) | return CountCalls(func) | ✅ 是(类装饰器) |
| 类本身 | return NewClass | ⚠️ 类装饰器,但较少见 |
| 非可调用对象 | return 42 / None / [] | ❌ 不是 |
二、从使用方式辨识
1.@语法糖(显性标识)
如果在代码中看到函数定义前有@符号,那它上面的就是装饰器:
@timer# ← timer 是装饰器@log_call# ← log_call 是装饰器defmy_func():pass注意:有@不代表一定"装饰成功"。如果装饰器返回的不是可调用对象,运行时会报错TypeError: 'NoneType' object is not callable。
2. 手动调用形式(等价写法)
即使没有@,如果看到这种写法,也说明该函数被当作装饰器使用:
my_func=timer(log_call(my_func))# 等价于叠加装饰器三、从行为模式辨识
1. "透明增强"原则
真正的装饰器遵循一个核心行为模式:对调用者透明。
即:
- 调用方式不变(参数签名保持一致)
- 返回值类型不变(或兼容)
- 函数名和文档应尽量保留(通过
@wraps)
@timerdefadd(a,b):returna+b# 调用方式完全不变result=add(2,3)# 调用者感知不到被装饰过如果返回的函数改变了原函数的调用方式(比如要求额外参数),那它更像是一个函数工厂而非装饰器。
2. 是否包装(Wrap)原函数
装饰器的本质是做包装(Wrapper),典型结构是:
defwrapper(*args,**kwargs):# 前置逻辑(可选)result=func(*args,**kwargs)# ← 必须调用原函数# 后置逻辑(可选)returnresult四、实用辨识技巧
技巧 1:用inspect检查
importinspectdefis_decorator(func):"""粗略判断:是否为高阶函数"""sig=inspect.signature(func)params=list(sig.parameters.values())# 至少有一个参数,且该参数可能是函数(无法100%确定,但可辅助判断)ifnotparams:returnFalse# 检查是否有 return 语句返回内部函数(静态检查有限)returnTrue# 仅作启发式判断技巧 2:运行时验证
如果你拿到一个函数,不确定它是不是装饰器,可以用测试函数验证:
deftest_target():return"original"# 假设 candidate 是你想验证的函数try:result=candidate(test_target)# 检查结果是否是 callable,且调用后行为正常ifcallable(result)andresult()=="original":print("✅ 很可能是装饰器")else:print("❌ 不是装饰器,或破坏了原函数")exceptExceptionase:print(f"❌ 不是装饰器:{e}")五、常见混淆情况辨析
| 代码模式 | 是不是装饰器 | 原因 |
|---|---|---|
def foo(func): return func | ⚠️ 退化装饰器 | 结构上符合,但无增强逻辑,称为"空装饰器"或"恒等装饰器" |
def foo(func): return lambda: func() | ✅ 是 | 返回包装函数,符合定义 |
def foo(): def inner(): pass; return inner | ❌ 不是 | 不接受函数参数,是普通工厂函数 |
def foo(func, times=3): ... | ⚠️ 需看用法 | 如果times有默认值且能直接@foo使用,是;如果必须@foo(3)才是装饰器,那是"装饰器工厂" |
六、一句话总结
装饰器的判断标准:一个函数,接收另一个函数作为参数,返回一个可调用对象,且返回的对象在调用时最终会执行原函数的逻辑。
用公式表达:
装饰器 = 高阶函数(接收函数) + 返回可调用对象 + 透明包装原函数如果你是在阅读第三方库源码时遇到某个函数,按上述结构特征(是否接收函数参数、是否返回函数)和使用场景(是否被@使用)结合判断,就能准确辨识。
4-Python装饰器(Decorator)学习笔记
📚 核心概念
装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能。
简单理解
原始函数: 煮面条 装饰器: 加个荷包蛋 结果: 荷包蛋面条 (面条 + 蛋 = 增强版面条)🎯 装饰器的5个层次
1️⃣ 基础装饰器
defmy_decorator(func):defwrapper():# 前置处理result=func()# 后置处理returnresultreturnwrapper@my_decoratordefsay_hello():print("Hello!")核心逻辑:
- 装饰器是一个函数,接受函数作为参数
- 返回一个新的函数(wrapper)
- 在wrapper中调用原函数
2️⃣ 保留元信息 (使用@wraps)
fromfunctoolsimportwrapsdefbetter_decorator(func):@wraps(func)defwrapper(*args,**kwargs):returnfunc(*args,**kwargs)returnwrapper为什么需要@wraps?
- 保留原函数的
__name__、__doc__等属性 - 让调试和反射更准确
3️⃣ 带参数的装饰器(装饰器工厂)
defrepeat(times):"""这返回一个真正的装饰器"""defdecorator(func):defwrapper(*args,**kwargs):for_inrange(times):func(*args,**kwargs)returnwrapperreturndecorator@repeat(times=3)defsay_hi():print("Hi!")关键理解:
@repeat(times=3)实际上先调用repeat(3)返回decorator- 然后
decorator再装饰函数 - 三层结构:参数函数 → 装饰器 → wrapper
4️⃣ 参数验证装饰器(类似你的需求)
deftool_parameters(parameters):defdecorator(func):defwrapper(**kwargs):# 验证必需参数required=parameters.get("required",[])forparaminrequired:ifparamnotinkwargs:raiseValueError(f"缺少必需参数:{param}")# 验证类型properties=parameters.get("properties",{})forname,valueinkwargs.items():expected_type=properties[name]["type"]# 检查类型...returnfunc(**kwargs)returnwrapperreturndecorator@tool_parameters({"type":"object","properties":{"path":{"type":"str"}},"required":["path"],})defread_file(path):returnf"读取:{path}"对应你的TypeScript示例:
// TypeScript@tool_parameters({"type":"object","properties":{"path":{"type":"string"}},"required":["path"],})# Python(功能等价)@tool_parameters({"type":"object","properties":{"path":{"type":"str"}},"required":["path"],})5️⃣ 实用装饰器示例
# 测量执行时间defmeasure_time(func):@wraps(func)defwrapper(*args,**kwargs):start=time.time()result=func(*args,**kwargs)end=time.time()print(f"耗时:{end-start:.4f}秒")returnresultreturnwrapper# 缓存结果fromfunctoolsimportlru_cache@lru_cache(maxsize=128)deffibonacci(n):ifn<2:returnnreturnfibonacci(n-1)+fibonacci(n-2)🔑 核心要点总结
装饰器 = 高阶函数
- 接受函数作为参数
- 返回一个新的函数
语法糖
@decoratordeffunc():pass# 等价于func=decorator(func)三层结构(带参数时)
parameter_func(parameters) → decorator → wrapper → original_func必须使用@wraps
- 保留原函数的元信息
- 方便调试和反射
常见用途
- 日志记录
- 性能测量
- 参数验证
- 缓存
- 权限检查
- 重试机制
🚀 运行示例
python main.py查看6个不同层次的装饰器示例,从最基础到类似@tool_parameters的参数验证装饰器。