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

Python装饰器进阶:深入理解与最佳实践

1、什么是装饰器

https://docs.python.org/zh-cn/3.7/glossary.html#term-decorator
官网介绍的很简单,返回值为另一个函数的函数,通常使用 @wrapper 语法形式来进行函数变换。装饰器就是闭包的应用,是用来**装饰(修改或增强)**函数、方法、类。

import timedef runtime(function):"""统计运行时间"""def wrapper():"""装饰函数"""start_time = time.time()function()print(f"runtime is {time.time() - start_time}")return wrapperdef fetch_http_data():print('开始请求网络数据')time.sleep(1)print('数据请求完成')@runtime
def parse_response_data():"""解析数据"""print('开始解析数据')time.sleep(0.5)print('数据解析完成')# 把函数当作参数传到另一个函数中执行,但是这种会改变调用方式
decorator = runtime(fetch_http_data)
decorator()# 使用语法糖,不会改变调用方式
parse_response_data()# 被装饰的函数,查看函数名的时候 变成了wrapper,所以装饰器会改变原函数的一些属性
print(parse_response_data.__name__) # wrapper

2、保留装饰器中函数的元数据

parse_response_data.**name** #wrapper
parse_response_data.**doc** # “”“装饰函数”“”
被装饰的函数,查看函数名的时候 变成了wrapper,函数的文档注释也改变了, 所以装饰器会改变原函数的一些属性,如何保留原函数的属性呢?

from functools import wraps# @wrap,它会帮助保留原函数的元信息
# @wraps 有一个重要特征是它能让你通过属性 __wrapped__ 直接访问被包装函数
# parse_response_data.__wrapped__() 可以调用原函数def runtime(function):"""统计运行时间"""@wraps(function)def wrapper():start_time = time.time()function()print(f"runtime is {time.time() - start_time}")return wrapper@runtime
def parse_response_data():print('开始解析数据')time.sleep(0.5)print('数据解析完成')print(parse_response_data.__name__)  # parse_response_data
print(parse_response_data.__doc__)  # 解析数据
# 通过__wrapped__属性调用原函数 
parse_response_data.__wrapped__()

3、带参数的装饰器

装饰器可以带参数,这样可以使装饰器更加灵活和通用,根据不同的情况对被装饰的函数应用不同的行为

def retry(max_retries=3):def decorator(func):def wrapper(*args, **kwargs):for retry_count in range(max_retries):try:result = func(*args, **kwargs)return resultexcept Exception as e:if retry_count < max_retries:print(f"Retrying {func.__name__} (attempt {retry_count + 1}/{max_retries})...")time.sleep(2)else:raise ereturn wrapperreturn decorator@retry(max_retries=2)
def potentially_failing_function():import randomif random.random() < 0.7:print("Function succeeded!")else:raise Exception("Function failed.")potentially_failing_function()

retry 装饰器接受一个 max_retries 参数,用于指定最大的重试次数。decorator 函数接受被装饰的函数 func,并定义了 wrapper 包装函数,该包装函数尝试执行 func,如果遇到异常则进行重试,最多尝试 max_retries 次。
然后,potentially_failing_function 函数应用了带参数的装饰器,允许在最多 2 次重试之后终止或成功执行。

4、 带可选参数的装饰器

import timedef timing_decorator(func=None, message="Execution time"):def decorator(wrapped_func):def wrapper(*args, **kwargs):start_time = time.time()result = wrapped_func(*args, **kwargs)end_time = time.time()execution_time = end_time - start_timeprint(f"{message}: {execution_time} seconds")return resultreturn wrapperif func is None:return decoratorelse:return decorator(func)@timing_decorator(message="Function 1 took")
def function1():time.sleep(2)print("Function 1 completed.")@timing_decorator
def function2():time.sleep(1)print("Function 2 completed.")function1()
function2()

5、 用类实现装饰器

除了使用函数实现装饰器外,类也可以实现装饰器,

import time
import functoolsclass runtime:def __init__(self, func):self.func = func# 保留被装饰函数的元数据functools.update_wrapper(self, func)def __get__(self, instance, owner):if instance is None:return self# 创建一个可调用的对象,将 instance作为self的参数传递进去return functools.partial(self, instance)def __call__(self, *args, **kwargs):start_time = time.time()result = self.func(*args, **kwargs)end_time = time.time()execution_time = end_time - start_timeprint(f"{self.func.__name__} took {execution_time} seconds")return result@runtime
def some_function():time.sleep(2)print("Function completed.")class Animal:@runtimedef walk(self, road):time.sleep(2)print(f"{self.__class__} walk {road}")some_function()
a = Animal()
a.walk('马路')

6、 类中的方法实现装饰器

# 类中的普通方法实现一个装饰器
class DecoratedMeta:def runtime(self, function):"""统计运行时间"""@functools.wraps(function)def wrapper(*args, **kwargs):"""装饰函数"""start_time = time.time()result = function(*args, **kwargs)print(f"runtime is {time.time() - start_time}")return resultreturn wrapper@classmethoddef runtime_cls(cls, function):@functools.wraps(function)def wrapper(*args, **kwargs):print('使用类方法的装饰器')return function(*args, **kwargs)return wrapperd = DecoratedMeta()@d.runtime  # 使用装饰器
def add(x, y):return x + y@d.runtime_cls
def sub(x, y):return x - yresult = add(2, 3)
print(result)
result = sub(4, 5)
print(result)

7、 给类加上装饰器

1、给类中的方法加装饰器

import timedef runtime(function):"""统计运行时间"""def wrapper(*args, **kwargs):"""装饰函数"""start_time = time.time()function(*args, **kwargs)print(f"runtime is {time.time() - start_time}")return wrapperclass Animal:@runtimedef walk(self):time.sleep(2)print(f"{self.__class__} walk")a = Animal()
a.walk()

2、给类加装饰器,扩充类的功能

# 定义一个装饰器函数
def log_decorator(cls):# 保存原始类的构造函数original_init = cls.__init__# 定义一个新的构造函数,扩充功能def new_init(self, *args, **kwargs):# 首先调用原始构造函数original_init(self, *args, **kwargs)# 扩展功能:在构造对象时打印信息print(f"创建 {self.__class__.__name__}")# 将新的构造函数替换原始构造函数cls.__init__ = new_initreturn cls# 使用装饰器扩充类的功能
@log_decorator
class MyClass:def __init__(self, x, y):self.x = xself.y = ydef add(self):return self.x * self.y# 创建类的实例
obj = MyClass(3, 4)# 扩充功能生效,构造对象时打印信息
result = obj.add()
print(result)

8、装饰器的叠加

import time
from functools import wrapsdef runtime(function):"""统计运行时间"""@wraps(function)def wrapper(*args, **kwargs):"""装饰函数"""start_time = time.time()result = function(*args, **kwargs)print(f"runtime is {time.time() - start_time}")return resultreturn wrapperdef printlog(function):"""函数运行日志:param function::return:"""@wraps(function)def wrapper(*args, **kwargs):print(f"{function.__name__} start")result = function(*args, **kwargs)print(f"{function.__name__} over")return resultreturn wrapper@printlog
@runtime
def add(x, y):time.sleep(0.5)return x + ydef sub(x, y):return x - y# 调用过程
# a = runtime(add)
# b = printlog(a)
# b(1,3)add(1, 3)a = runtime(sub)
b = printlog(a)
res = b(1, 3)
print(res)

9、 内置的装饰器

@classmethod
把一个方法封装成类方法。

# python的redis第三方库中,使用url连接redis时定义的from_url是一个类方法。
class Redis(object):""""""@classmethoddef from_url(cls, url, db=None, **kwargs):""""""connection_pool = ConnectionPool.from_url(url, db=db, **kwargs)return cls(connection_pool=connection_pool)# 调用类方法
redis_ints = Redis.from_url('redis://user:password@127.0.0.1:6379/0')

@staticmethod
将方法转换为静态方法。

import mathclass CrawlSite:# 使用静态方法计算页数,与实例无关,工具方法@staticmethoddef get_page(total, offsize):"""计算要爬取的页数"""return math.ceil(total / offsize)

@property
会将方法转化为一个具有相同名称的只读属性的 “getter”,特征属性对象具有 getter, setter 以及 deleter 方法,它们可用作装饰器来创建该特征属性的副本,并将相应的访问函数设为所装饰的函数

class Animal(object):def __init__(self, eat):self.__eat = eat# 只有@property时属性不能赋值操作@propertydef eat(self):return self.__eat@eat.setterdef eat(self, value):# 设置属性值,同时可以做校验、计算等if not isinstance(value, str):raise TypeError('Expected a string')self.__eat = value@eat.deleterdef eat(self):del self.__eata = Animal('rot')
print(a.eat)
a.eat = 'cao'
print(a.eat)

@functools.wraps
保留被装饰的函数元信息,用于在定义包装器函数时发起调用 update_wrapper() 作为函数装饰器
@functools.lru_cache
一个为函数提供缓存功能的装饰器,如果调用相同,则直接返回缓存中的值,不需要重新计算。用以节约高开销或I/O函数的调用时间。
如果 maxsize 设置为 None ,LRU功能将被禁用且缓存数量无上限。由于使用了字典存储缓存,所以该函数的固定参数和关键字参数必须是可哈希的。

@lru_cache(maxsize=100)  
def fibonacci(n):  if n < 2:  return n  return fibonacci(n-1) + fibonacci(n-2)  print(fibonacci(10))  # 输出 55  
print(fibonacci(10))  # 输出 55,不会重新计算

@functools.singledispatch
实现只有第一个参数可接受不同类型的函数

from functools import singledispatch@singledispatch
def calculate_area(argument):raise NotImplementedError('Unsupported operand type')@calculate_area.register(int)
def _(argument):return argument * argument@calculate_area.register(str)
def _(argument):return int(argument) * int(argument)print(calculate_area(5))  # 输出 25
print(calculate_area('6'))  # 输出 36

@contextlib.contextmanager
它可以定义一个支持 with 语句上下文的工厂函数, 而不需要创建一个类或区 enter() 与 exit() 方法。

import contextmanager
import timedef adds():for i in range(3):print(i)time.sleep(1)@contextlib.contextmanager
def timing_context(func):start_time = time.time()try:func()yield 'runtime'  # 进入上下文 yield后面的值,就会赋在 with语句的as 后面finally:end_time = time.time()elapsed_time = end_time - start_timeprint(f"Elapsed time: {elapsed_time} seconds")# 使用上下文管理器来测量代码块的执行时间
with timing_context(adds) as msg:# 模拟耗时操作print(msg)

10、 自定义常用的装饰器

重试机制

import functools
import timedef retries(max_retries=3, delay=1):def decorator(func):@functools.wraps(func)def wrapper(*args, **kwargs):retry_count = 0while retry_count < max_retries:try:return func(*args, **kwargs)except Exception as e:print(f"Error: {func.__name__} failed with {e}. Retrying in {delay} seconds...")retry_count += 1time.sleep(delay)raise Exception(f"Error: {func.__name__} failed after {max_retries} retries.")return wrapperreturn decorator@retries()
def some_function():# Some code that might fail.print('----------------')@retries(max_retries=5, delay=3)
def another_function():# Some code that might fail.print('=============')raisesome_function()another_function()

超时判断

import timeimport functools
from concurrent import futurespool = futures.ThreadPoolExecutor(1)def runtime(seconds):def decorator(func):@functools.wraps(func)def wrapper(*args, **kw):future = pool.submit(func, *args, **kw)return future.result(timeout=seconds)return wrapperreturn decorator@runtime(3)
def request_http():time.sleep(2)return 111res = request_http()
print(res)

相关文章:

Python装饰器进阶:深入理解与最佳实践

1、什么是装饰器 https://docs.python.org/zh-cn/3.7/glossary.html#term-decorator 官网介绍的很简单&#xff0c;返回值为另一个函数的函数&#xff0c;通常使用 wrapper 语法形式来进行函数变换。装饰器就是闭包的应用&#xff0c;是用来**装饰&#xff08;修改或增强&…...

数据库数据恢复—Oracle数据库报错ORA-01110错误的数据恢复案例

Oracle数据库故障&#xff1a; 北京某公司一台运行oracle数据库的服务器&#xff0c;机房意外断电导致该服务器重启&#xff0c;重启后发现oracle数据库报错。该Oracle数据库没有备份。 Oracle数据库数据恢复过程&#xff1a; 1、北亚企安数据恢复工程师检查该oracle数据库的数…...

如何通过adb控制安卓手机wifi

一、准备工作 1、先用USB数据线 将手机和电脑连接在一起 1&#xff09; 数据线连接手机和电脑&#xff0c;选择“传输文件”的连接方式&#xff1b; 2&#xff09; 在手机上&#xff0c;打开“开发者选项”、“USB调试”&#xff1b; 2、在电脑上安装adb工具&#xff0c;参考…...

VR全景应用广泛体现在哪里?有何优势?

VR全景作为一种新型营销方式&#xff0c;正在逐渐走进人们的视线&#xff0c;它区别于以往单一角度的照片和视频&#xff0c;VR全景制作显得更加直观、更加真实、更加生动。VR全景通过VR技术将所拍摄的图片变成720度可观看的场景模式&#xff0c;把产品的特色以及魅力整体呈现展…...

【深度学习】Python使用指定gpu运行代码

命令行指定显卡GPU运行python脚本 在大型机构分配的服务器集群中&#xff0c;需要使用GPU的程序默认都会在第一张卡上进行&#xff0c;如果第一张卡倍别人占用或者显存不够的情况下&#xff0c;程序就会报错说没有显存容量&#xff0c;所以能够合理地利用GPU资源能帮助你更快更…...

二叉树的遍历

树森林二叉树先序遍历先序遍历先序遍历后序遍历中序遍历中序遍历 1.前序遍历 leetcode题目链接 1.1 递归 前序遍历递归方式 class Solution { public:vector<int> preorderTraversal(TreeNode* root) {vector<int> res;if(root){res.push_back(root->val);ve…...

分布式限流:Redis

目录 1:如何实现分布式限流 2:限流的几种类别 2.1:固定窗口限流 2.2:滑动窗口限流 2.3:漏桶限流 2.4:令牌桶限流 3:实现分布式限流:Redis 3.1:引入Redisson的依赖包 3.2:初始化Redisson 3.3:创建Redisson的限流类 1:如何实现分布式限流 1:把统计用户的使用频率等这些…...

python excel接口自动化测试框架

前言 前些天写了pytestyamlallure接口自动化测试框架这篇文章。 今天采用Excel继续写一个接口自动化测试框架。 设计流程图 这张图是我的excel接口测试框架的一些设计思路。 首先读取excel文件&#xff0c;得到测试信息&#xff0c;然后通过封装的requests方法&#xff0c…...

Java开发面试--MongoDB专区

1、你是否了解 MongoDB&#xff1f; 答&#xff1a; 是的&#xff0c;我了解 MongoDB。MongoDB是一个流行的NoSQL数据库&#xff0c;它以文档的形式存储数据&#xff0c;具有高度的灵活性和可扩展性。我熟悉MongoDB的基本概念和特性&#xff0c;例如集合&#xff08;Collectio…...

当『后设学习』碰上『工程学思维』

只要我成为一个废物&#xff0c;就没人能够利用我&#xff01; 雷猴啊&#xff0c;我是一只临期程序猿。打过几年工&#xff0c;写过几行代码。但今天我不想聊代码&#xff0c;我们聊聊学习这件事。 技术年年更新&#xff0c;尤其是前端框架&#xff0c;很多时候觉得学习速度都…...

一表谈现实、系统、流程、报表与BI

序号主题描述1系统与现实1.1管理系统个体应用决定因素管理能力、软件工程与管理环境的综合结果1.2信息系统与现实业务的关系现实世界是以业务利弊为抓手&#xff1b;信息系统是以业务流程为抓手&#xff1b;信息系统只是对现实业务部分的数字化应用总结&#xff1a;现实业务是以…...

数据结构顺序栈例题一

内容仅供个人复习 #include<iostream> #define MAXSIZE 100using namespace std;typedef struct {int *base;int *top;int stackSize;}SqStack;void Initstack( SqStack &s) // 初始化栈 {s.base new int[MAXSIZE];s.top s.base;s.stackSize MAXSIZE; }void Pus…...

大模型在百度智能问答、搜索中的应用

本文主要介绍了智能问答技术在百度搜索中的应用。包括机器问答的发展历程、生成式问答、百度搜索智能问答应用。欢迎大家加入百度搜索团队&#xff0c;共同探索智能问答技术的发展方向&#xff0c;文末有简历投递方式。 01 什么是机器问答 机器问答&#xff0c;就是让计算机…...

ARPG----C++学习记录01日志和调试

多人射击有点难&#xff0c;发现这个更加基础&#xff0c;先学习这个 显示日志 可以在代码中插入这样一行来打印日志&#xff0c;蓝图里的printstring会在屏幕和日志里都显示。可以使用%f&#xff0c;d等来获取后边的输入值。对于打映字符串变量&#xff0c;传入需要* UE_LOG…...

3302. 表达式求值, 栈的应用

3302. 表达式求值 - AcWing题库 给定一个表达式&#xff0c;其中运算符仅包含 ,-,*,/&#xff08;加 减 乘 整除&#xff09;&#xff0c;可能包含括号&#xff0c;请你求出表达式的最终值。 注意&#xff1a; 数据保证给定的表达式合法。题目保证符号 - 只作为减号出现&…...

论文写作框架示例:论软件系统建模方法及其应用

标题 前言题目要求写作框架(1)摘要(300~330字)(2)正文(2000~2500字,2200字左右为宜)(3)收尾(200字左右)前言 本章内容参考了51cto的薛老师的《软考论文高分特训与范文10篇》的内容,是帮助初学者打开写作思路的工具,而不是必须要遵循的模式。建议软考人多读多看…...

Godot 官方2D C#重构(4):TileMap进阶使用

文章目录 前言完成内容项目节点结构TileMap设置图片资源备选图片添加物理碰撞添加y轴遮罩判断Y Sort Enable是干什么的&#xff1f; 脚本代码 前言 Godot 官方 教程 Godot 2d 官方案例C#重构 专栏 Godot 2d 重构 github地址 完成内容 项目节点结构 TileMap设置 图片资源 备选图…...

Ubuntu系统编译调试QGIS源码保姆级教程

在之前的文章中&#xff0c;我详细介绍了怎么在Windows下编译QGIS源码&#xff0c;也得到了不错的反馈。但是不足的是Windows下只能编译QGIS的Release模式和RelWithDebInfo模式&#xff0c;想要分析源码&#xff0c;“断点调试”肯定是少不了的&#xff0c;但是这两种模式虽然也…...

电源控制系统架构(PCSA)之系统控制处理器

安全之安全(security)博客目录导读 目录 一、系统控制处理器 1、服务 2、可信操作 一、系统控制处理器 SCP是一种基于处理器的能力&#xff0c;为提供电源管理功能和服务提供了一个灵活和可扩展的平台。 在移动系统中&#xff0c;SCP处理器一般是Cortex-M微控制器&#xff…...

深度学习之轻量级神经网络在TWS蓝牙音频处理器上的部署

加我微信hezkz17进数字音频系统研究开发交流答疑群(课题组) 深度学习之轻量级神经网络在TWS蓝牙音频处理器上的部署 深度学习之轻量级神经网络在TWS蓝牙音频处理器上的部署 深度学习之轻量级神经网络在TWS蓝牙音频处理器上的部署 项目一 科大讯飞经验 在Matlab平台上实现广义…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

Spring Boot 实现流式响应(兼容 2.7.x)

在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力

引言&#xff1a; 在人工智能快速发展的浪潮中&#xff0c;快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型&#xff08;LLM&#xff09;。该模型代表着该领域的重大突破&#xff0c;通过独特方式融合思考与非思考…...

EtherNet/IP转DeviceNet协议网关详解

一&#xff0c;设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络&#xff0c;本网关连接到EtherNet/IP总线中做为从站使用&#xff0c;连接到DeviceNet总线中做为从站使用。 在自动…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

基于matlab策略迭代和值迭代法的动态规划

经典的基于策略迭代和值迭代法的动态规划matlab代码&#xff0c;实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

初探Service服务发现机制

1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能&#xff1a;服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源&#xf…...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合

在汽车智能化的汹涌浪潮中&#xff0c;车辆不再仅仅是传统的交通工具&#xff0c;而是逐步演变为高度智能的移动终端。这一转变的核心支撑&#xff0c;来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒&#xff08;T-Box&#xff09;方案&#xff1a;NXP S32K146 与…...