当前位置: 首页 > ds >正文

python装饰器的简单理解

python装饰器可分为函数装饰器和类装饰器,在理解过程中,可以认为是相同的。

一、不带参数的函数装饰器

def my_decorator(func):def wrapper():print("函数执行前")func()print("函数执行后")return wrapper

这里my_decorator就是我们定义的一个不带参数的装饰器(其本身就是一个函数),

使用如下:

@my_decorator
def say_hello():print("Hello!")

上述代码段可以简单的理解为say_hello=my_decorator(say_hello),即将say_hello函数作为参数传递给装饰器函数,并返回装饰器函数中定义的函数名。因此,say_hello=wrapper

当我们执行say_hello()时,相当于执行wrapper()
执行结果如下:

say_hello()输出:
复制函数执行前
Hello!
函数执行后

二、带参数的函数装饰器

定义带参数的函数装饰器

def repeat(num_times):def decorator(func):def wrapper(*args, **kwargs):for _ in range(num_times):result = func(*args, **kwargs)return resultreturn wrapperreturn decorator

使用如下:

@repeat(3)
def greet(name):print(f"Hello, {name}!")

这里,@repeat(3)相当于@decorator(可以简单的理解为,先将参数传递给外部装饰器repeat,然后返回真正的装饰器decorator)
因此 上述代码可以看做greet=decorator(greet)greet = wrapper 只是相比于不带参数的装饰器多传递了一个参数。

当我们执行greet()时,同样相当于执行wrapper()
执行实例如下:

greet("Alice")
输出:
Hello, Alice!
Hello, Alice!
Hello, Alice!

三、类装饰器

定义类装饰器

class MyDecorator:def __init__(self, func):self.func = funcdef __call__(self, *args, **kwargs):print("函数执行前")result = self.func(*args, **kwargs)print("函数执行后")return result

调用如下:

@MyDecorator
def say_hello():print("Hello!")

上述代码可以直接理解为say_hello = MyDecorator(say_hello) 因此,当使用say_hello()时,相当于直接调用__call__()函数

执行实例如下:

say_hello()输出:
函数执行前
Hello!
函数执行后

四、装饰器作用于类定义

定义装饰器:

def add_class_name(cls):# 在类上添加一个属性,并返回原类cls.class_name = 'person class'return cls

调用如下:

@add_class_name          
class Person:def __init__(self, name):self.name = name

上述代码可以等价于Person = add_class_name(Person),那么此时调用Person就相当于调用add_class_name(Person)

执行实例

A = Person()

上面这行代码相当于在运行A = add_class_name( Person)运行的结果,就是返回了一个被装饰过的新的cls,因此新的cls有了新的属性class_name,我们就可以调用 print(A.class_name) # 输出: "person class"

五、 装饰器的作用

个人理解:

  1. 增加代码复用:装饰器中封装一些通用的功能,定义不同函数时都可以利用装饰器中的功能。(固定装饰器,原函数不固定);
  2. 为原函数拓展功能:不改动原函数中的代码,例如:第一节中的say_hello()函数作为无法改动的原函数,那么我们可以通过改动装饰器中的功能,为原函数添加拓展功能(固定原函数,改动装饰器);

六、应用场景

Python 中装饰器的应用场景主要用于代码复用逻辑抽离横切关注点(如日志、验证、缓存、安全控制等)。下面是一些典型应用场景和具体示例:


6.1 日志记录(Logging)

用途:记录函数或方法的调用行为,便于调试和分析。

def log(func):def wrapper(*args, **kwargs):print(f"[LOG] Calling {func.__name__} with args={args}, kwargs={kwargs}")result = func(*args, **kwargs)print(f"[LOG] {func.__name__} returned {result}")return resultreturn wrapper@log
def add(a, b):return a + badd(2, 3)

6.2. 权限验证(Authentication/Authorization)

用途:用于 Web 应用、命令行工具中限制某些函数的访问。

def require_admin(func):def wrapper(user, *args, **kwargs):if not user.get("is_admin"):raise PermissionError("You must be an admin.")return func(user, *args, **kwargs)return wrapper@require_admin
def delete_user(user, user_id):print(f"Deleting user {user_id}")

6.3. 函数缓存(Memoization)

用途:缓存计算结果,加速函数响应,避免重复计算。

from functools import lru_cache@lru_cache(maxsize=128)
def fibonacci(n):if n < 2:return nreturn fibonacci(n - 1) + fibonacci(n - 2)print(fibonacci(40))  # 快速返回

6.4. 性能计时(Profiling)

用途:度量函数执行时间,用于性能分析。

import timedef timer(func):def wrapper(*args, **kwargs):start = time.time()res = func(*args, **kwargs)end = time.time()print(f"{func.__name__} took {end - start:.4f}s")return resreturn wrapper@timer
def slow_function():time.sleep(1)return "Done"slow_function()

6.5. 类注册与插件机制

用途:动态注册类或函数用于工厂、插件系统。

registry = {}def register(name):def decorator(cls):registry[name] = clsreturn clsreturn decorator@register("dog")
class Dog:def speak(self):return "Woof"print(registry["dog"]().speak())  # 输出 "Woof"

6.6. 输入验证 / 参数检查

用途:自动检查输入是否符合预期。

def validate_non_negative(func):def wrapper(x):if x < 0:raise ValueError("Negative value not allowed")return func(x)return wrapper@validate_non_negative
def sqrt(x):return x ** 0.5

6.7. 类装饰器(用于元编程、修改类行为)

用途:为类增加静态属性、方法、注册机制等。

def add_repr(cls):def __repr__(self):return f"{cls.__name__}({self.__dict__})"cls.__repr__ = __repr__return cls@add_repr
class Person:def __init__(self, name):self.name = nameprint(Person("Alice"))
http://www.xdnf.cn/news/8738.html

相关文章:

  • (2)-玩转Fiddler抓包-再识Fiddler
  • 天地图实景三维数据分享(江苏)
  • 【iOS】内存分区
  • 第2周 PINN核心技术揭秘: 如何用神经网络求解偏微分方程
  • 消息中间件之kafka
  • WSL 下面 Buildroot + QEMU 环境记录一下
  • [特殊字符] 使用增量同步+MQ机制将用户数据同步到Elasticsearch
  • Linux(6)——第一个小程序(进度条)
  • LeetCode 2942.查找包含给定字符的单词:使用库函数完成
  • 台式机安装新的固态硬盘后无显示
  • 【C语言练习】060. 使用指针操作字符串
  • Kotlin全栈工程师转型路径
  • Vue-创建应用/挂载应用/根组件模版-.vue单文件/应用配置
  • Cesium中根据不同条件设置3D Tiles样式
  • 【VBA 中GetOpenFilename】常用友好的人机交互文件全路径选择模式
  • 计算机视觉与深度学习 | 基于 YOLOv8 + BeautyGAN + CodeFormer + Face Parsing 实现简单的人脸美颜
  • 【来自纳米AI-大模型】ubuntu 24.04 登陆界面分辨率太高,内容显示得特别小 问题解决方案(亲测有效)
  • lua脚本学习笔记1:Vscode添加lua环境_lua基本语法
  • HarmonyOS赋能套件介绍
  • 开篇:MCP理论理解和学习
  • 元组可以比较大小吗?一次返回多个值?编程语言的元组?声明变量一定需要指定类型吗?
  • Ubuntu20.04 gr-gsm完整安装教程
  • Kanass V1.1.1版本发布,支持查看项目/迭代/事项进度
  • 剖析 Spring 中 @ResponseBody 原理与 Tomcat NIO 写事件(SelectionKey.OP_WRITE)的协作机制
  • MySQL分库分表
  • vue3中使用computed
  • Spark集群架构解析:核心组件与Standalone、YARN模式深度对比(AM,Container,Driver,Executor)
  • kafka之操作示例
  • 大文件上传,对接阿里oss采用前端分片技术。完成对应需求!
  • 【MySQL】第7节|Mysql锁机制与优化实践以及MVCC底层原理剖析