七个常用<python装饰器>---足够改进代码质量 (保姆详解)

前言:

写代码嘛,关键是得让它既好用又好看,这不,Python装饰器就摆在那儿。咱们程序员有时也得有那么点艺术家的腔调:讲求效率,追求代码的简洁优雅,偶尔还得装装X,不是吗?
翻开人家的工程代码,哎呦,有时候我都忍不住想吐槽,怎么就这么啰嗦?这不,咱就得拿出刀子,削削刻刻,把装饰器这玩意儿的妙用给咱整明白了。得让代码那叫一个流畅,看着清清楚楚,懂得一看就会,会了还想看。

正文:

1. @staticmethod

作用

@staticmethod 装饰器允许我们定义一个方法,使其不依赖于对象的状态(即不与特定的实例self绑定,也不与类cls绑定)。这主要用在你想将某个功能置于类的命名空间(namespace)下,以表明它逻辑上属于类,但实际上又独立于类的任何特定实例。


用法

当你需要直接通过类调用方法而不需要实例时,将该方法定义为静态方法是非常合适的。
class MathOperations:@staticmethoddef add(x, y):"""返回两个参数的和。"""return x + y# 调用静态方法,注意如何使用类名直接调用静态方法,而不是类的实例
result = MathOperations.add(5, 10)
print(result)  # 输出: 15

代码讲解

在上述代码中,add 方法被 @staticmethod 装饰,意味着它不接受常规方法第一个参数(self)。这使得它可以像普通函数一样通过类名直接调用,而不需要创建类的实例。


对比

不使用 @staticmethod 的普通方法会要求传递一个实例作为第一个参数。下面是未使用 @staticmethod 的对比:
class MathOperations:def add(self, x, y):"""返回两个参数的和。需要通过实例调用。"""return x + y# 创建类的实例
math_ops = MathOperations()
result = math_ops.add(5, 10)
print(result)  # 输出: 15

在这个例子中,我们必须先创建一个 MathOperations 类的实例,并通过这个实例调用 add 方法。


2.@classmethod

作用

@classmethod 装饰器使方法变成类方法,它接收类本身作为第一个参数,通常命名为 cls。这使得这个方法可以访问类属性或调用其他类方法,而不依赖于具体的实例。


用法

类方法通常用作替代构造函数。
class MyClass:_my_list = []@classmethoddef add_to_list(cls, item):"""将项目添加到类属性列表中。"""cls._my_list.append(item)MyClass.add_to_list('item 1')
print(MyClass._my_list)  # 输出: ['item 1']

代码讲解

在上述代码中,add_to_list 方法是一个类方法,可以直接通过类来调用,并且它修改了类属性 _my_list。


对比

常规的实例方法需要创建实例才能调用,并且只能访问实例属性:
class MyClass:def __init__(self):self._my_list = []def add_to_list(self, item):"""将项目添加到实例属性列表中。"""self._my_list.append(item)# 创建类的实例
my_instance = MyClass()
my_instance.add_to_list('item 1')
print(my_instance._my_list)  # 输出: ['item 1']# 尝试使用类名直接调用将会失败
# MyClass.add_to_list('item 1')  # 这将抛出 TypeError

3. @property

作用

@property 装饰器用于将类中的方法当作一个属性来访问,通常用于计算或返回内部状态的值,同时不让调用者直接访问内部状态。


用法

它经常被用于创建只读属性。
class MyClass:def __init__(self, value):self._internal_state = value@propertydef internal_state(self):"""访问内部状态的一个只读属性"""return self._internal_state# 创建类实例
instance = MyClass(5)
print(instance.internal_state)  # 输出: 5

代码讲解

在上面的代码中,internal_state 成为一个只读属性,我们不能直接对它赋值(除非也定义了 setter)。


对比

没有 @property 装饰器,我们通常会直接公开属性或通过一个明确的 getter 方法来访问:
class MyClass:def __init__(self, value):self.internal_state = value# 创建类实例
instance = MyClass(5)
print(instance.internal_state)  # 输出: 5

在这个例子中,internal_state 可以被外部直接访问和修改,没有了 @property 提供的保护层。

4. @property.setter

作用

@property.setter 装饰器是与之前的 @property 配套使用的,它允许我们定义一个赋值方法,可以通过这个属性对相关数据进行设置。


用法

我们可以用它来定义一个属性的设置逻辑,例如实施类型检查或值验证。
class MyClass:def __init__(self, value):self._internal_state = value@propertydef internal_state(self):return self._internal_state@internal_state.setterdef internal_state(self, value):if not isinstance(value, int):raise ValueError("internal_state must be an integer")self._internal_state = value# 创建类实例
instance = MyClass(5)
instance.internal_state = 10  # 有效
print(instance.internal_state)  # 输出: 10

代码讲解

在这个例子中,尝试为 internal_state 赋非整数值会引发 ValueError,因为我们的 setter 方法中添加了类型检查。


对比

如果没有 @property.setter,我们不能定义一个属性的赋值行为,属性将是完全公开的:
class MyClass:def __init__(self, value):self.internal_state = value# 创建类实例
instance = MyClass(5)
instance.internal_state = 'a string'  # 没有问题,但可能不是我们想要的行为

在没有 setter 装饰器的情况下,internal_state 属性可以接受任何类型的赋值,没有额外的验证逻辑来保护类的内部表示。

5. @functools.wraps

作用

装饰器在Python中用于增加函数额外的功能,但装饰器会改变函数的__name__和__doc__属性。@functools.wraps是一个特殊的装饰器,用于在定义装饰器时保持原函数的元数据不变。


用法

这通常用于自定义装饰器的编写中,以确保被装饰函数的元数据不会丢失。
import functoolsdef my_decorator(func):@functools.wraps(func)def wrapper(*args, **kwargs):print("函数发生前...")result = func(*args, **kwargs)print("函数发生后")return resultreturn wrapper@my_decorator
def say_hello():"""Greet the user."""print("Hello!")# 由于使用了 functools.wraps,say_hello 保留了其原始的元数据
print(say_hello.__name__)  
print(say_hello.__doc__)   

代码讲解

在这个例子中,functools.wraps用在内部函数wrapper上,保护了原始函数say_hello的__name__和__doc__属性。如果不使用functools.wraps,say_hello.__name__将会返回'wrapper'。


对比

没有@functools.wraps,函数say_hello的属性会被丢失:
def my_decorator(func):def wrapper(*args, **kwargs):"""Wrapper function for func."""print("函数get前发生的...")result = func(*args, **kwargs)print("函数get后发生的...")return resultreturn wrapper@my_decorator
def say_hello():print("Hello!")# 没有使用 functools.wraps,元数据被覆盖了
print(say_hello.__name__) 
print(say_hello.__doc__)   

在没有使用functools.wraps的这个版本中,装饰函数wrapper的__name__和__doc__覆盖了say_hello的元数据,这会在某些情况下导致混淆,特别是在需要函数签名保持原样时。

6. @functools.lru_cache

作用

@functools.lru_cache 提供了一个简单的缓存机制,让函数能缓存最近使用过的输入及其结果。如果一个函数被频繁调用,并且伴随相同的参数,LRU(Least Recently Used)缓存可以提升性能。


用法

它特别用于那些计算开销大但又有高重复调用概率的函数。
import functools@functools.lru_cache(maxsize=32)
def fibonacci(num):"""返回斐波那契数列的第 num 个数"""if num < 2:return numreturn fibonacci(num - 1) + fibonacci(num - 2)# 第一次调用将会缓存结果
print(fibonacci(10))  # 输出: 55
# 后续调用将会使用缓存
print(fibonacci(10))  # 输出: 55

代码讲解

在这个例子中,fibonacci 函数的每个结果会在首次计算后缓存起来。当你再次用同样的参数调用该函数时,它会返回缓存的结果而不是重新计算。


对比

不使用 @functools.lru_cache 运行上面的 fibonacci 函数会导致很多重复的计算,特别是参数较大时会非常耗时。

7. @functools.singledispatch

作用

@functools.singledispatch 装饰器可以将普通函数转化为单分派泛函数。这表示函数将会根据第一个参数的类型,调用另外专门定义的函数,实现类似于其他语言中的函数重载。


用法

它特别适用于需要根据不同类型执行不同操作的场景。
import functools@functools.singledispatch
def format_data(arg):"""默认的实现,被调用时参数类型没有匹配的特定注册函数"""return str(arg)@format_data.register
def _(arg: int):"""专门用于处理整数的实现"""return f"{arg} is an integer"@format_data.register
def _(arg: list):"""专门用于处理列表的实现"""return f"List length is {len(arg)} and items are {', '.join(map(str, arg))}"# 根据参数类型调用不同的实现
print(format_data("Hello!")) 
print(format_data(42))       
print(format_data([1, 2, 3])) 

代码讲解

在这个例子中,对于任意未注册类型,format_data 将使用默认实现,即直接转换为字符串。对于被注册类型,例如 int 和 list,将调用专为它们定义的函数。


对比

若没有 @functools.singledispatch,你通常需要编写显式的类型检查语句(如使用 if-elif-else 结构),这样的代码通常会更加冗长和不易维护。

总结:

好了,我的粉儿们,咱们今天的`装饰器课`就上到这儿。

记住,装饰器不光让你的代码行数变短,颜值变高,更重要的是,它能让你的工作流、逻辑变得更清晰

  1. @staticmethod@classmethod能帮你搞定静态和类相关的调用;
  2. @property 让访问方法像是读取属性一样优雅;
  3. @functools.wraps,让你自定义装饰器的时候还能保持原函数的尊严和名声。
  4. 然后是咱们的性能加速器@functools.lru_cache,让你对那些费劲儿的计算结果记个小本本,下次用的时候直接从记忆里找,不再重新煮沸冷饭,节约不少脑力。
  5. 至于@functools.singledispatch,简直就是动态语言Python的类型挂钩利器,让同一个名字的函数根据传参的不同表演不同的戏法。


啰嗦了这么多,希望你们明白:代码写得好不好,跟会不会用装饰器,那可是直接挂钩的。用好了,你的代码就像是精品咖啡,不用或者用糟了,那就是旅馆速溶。行了,回家练去吧。

保持好奇,保持饥渴,这样才能写出更香的代码。加油!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/210340.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringSecurity6 | 自定义认证规则

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; Java从入门到精通 ✨特色专栏&#xf…

移相干涉技术1-多种干涉条纹仿真模拟生成(原理转载+代码实现 包括模拟生成干涉条纹图)

过去的干涉测量技术是通过人的肉眼或者相机拍摄&#xff0c;来直观判断干涉图中条纹特征进而完成测量&#xff0c;该方法的不稳定因素&#xff08;比如人的主观意志&#xff09;很多&#xff0c;其精度误差在/10左右38]&#xff1b;现代干涉测量技术通过将电子技术、计算机技术…

智能优化算法应用:基于厨师算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于厨师算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于厨师算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.厨师算法4.实验参数设定5.算法结果6.参考文献7.MATLAB…

代码随想录-刷题第二十一天

530.二叉搜索树的最小绝对差 题目链接&#xff1a;530. 二叉搜索树的最小绝对差 思路&#xff1a;二叉搜索树的中序遍历是有序的。根据二叉搜索树的这个特性来解题。 class Solution {// 同样利用二叉树中序遍历是一个有序数组。private List<Integer> list new Arra…

一加 12 Pop-up快闪活动来袭,十城联动火爆开启

12 月 9 日&#xff0c;一加 12 Pop-up 快闪活动在北京、深圳、上海、广州等十城联动开启&#xff0c;各地加油欢聚快闪现场&#xff0c;抢先体验与购买一加 12。作为一加十年超越之作&#xff0c;一加 12 全球首发拥有医疗级护眼方案和行业第一 4500nit 峰值亮度的 2K 东方屏、…

C++新经典模板与泛型编程:策略类模板

策略类模板 在前面的博文中&#xff0c;策略类SumPolicy和MinPolicy都是普通的类&#xff0c;其中包含的是一个静态成员函数模板algorithm()&#xff0c;该函数模板包含两个类型模板参数。其实&#xff0c;也可以把SumPolicy和MinPolicy类写成类模板—直接把algorithm()中的两…

【Linux】无法使用 ifconfig 查看系统网络接口信息,报错 command not found: ifconfig

问题描述 ifconfig是一个用于配置和显示系统网络接口信息的命令行工具。它通常用于Unix、Linux和其他类Unix系统中。 通过ifconfig命令&#xff0c;你可以查看和修改系统中网络接口的配置信息&#xff0c;包括IP地址、子网掩码、MAC地址、MTU&#xff08;最大传输单元&#x…

javacv踩坑记录

前一阵学习opencv&#xff0c;发现在做人脸识别的时候遇到一些类库不存在的情况&#xff0c;查找后发现是由于拓展包没有安装完全&#xff08;仅安装了基础版&#xff09;。由于网络的问题&#xff08;初步猜测&#xff09;&#xff0c;始终无法安装好拓展包。 于是另辟蹊径&am…

MongoDb数据库

一、命令交互 1.1 数据库命令 1.显示所有数据库&#xff1a; show dbs 2.切换到指定数据库&#xff0c;如果没有则自动创建数据库 use databaseName 3.显示当前所在数据库 db 4.删除当前数据库 use 库名 db.dropDatabase() 1.2 集合命令 1.创建集合 db.createColl…

[文档级关系抽取|ACL论文]文档级关系抽取中语言理解的基础模型

Did the Models Understand Documents? Benchmarking Models for Language Understanding in Document-Level Relation Extraction School of Computer Science, Fudan University | ACL 2023.06 | 原文链接 Background 过去的工作大多数都是从单个句子中收获更多的关系&am…

MongoDB中的$type操作符和limit与skip方法

本文主要介绍MongoDB中的$type操作符和limit与skip方法。 目录 MongoDB的$type操作符MongoDB的limit方法MongoDB的skip方法 MongoDB的$type操作符 MongoDB中的$type操作符用于检查一个字段的类型是否与指定的类型相匹配。它可以用于查询和投影操作。 $type操作符可以与以下数…

php,redis实现一个电影热度排行榜

要实现电影热度排行榜&#xff0c;需要记录每个电影的热度值&#xff0c;热度值可以根据不同的算法计算&#xff0c;例如&#xff1a;观看次数、评分数、评论数等。这里我们以观看次数为例。 首先&#xff0c;需要使用 Redis 的 Sorted Set 数据结构来存储电影的热度值和电影 …

JVS低代码表单引擎:数据校验与处理的先锋

随着信息技术的迅速发展&#xff0c;数据校验与处理已经成为了各类应用中不可或缺的一环。尤其是在涉及敏感信息&#xff0c;如密码处理时&#xff0c;其安全性和准确性显得尤为重要。JVS低代码表单引擎提供了强大的文本组件触发逻辑校验功能&#xff0c;它能够在用户填写数据的…

截取字符串

输入一个字符串和一个整数 k &#xff0c;截取字符串的前k个字符并输出。 数据范围&#xff1a;字符串长度满足 1≤n≤1000&#xff0c; 1≤k≤n 输入描述&#xff1a; 1.输入待截取的字符串 2.输入一个正整数k&#xff0c;代表截取的长度 输出描述&#xff1a;截取后的字符串…

模电·放大电路的分析方法——等效电路法

放大电路的分析方法——等效电路法 晶体管的直流模型及静态工作点的估算法晶体管共射h参数等效模型 h h h参数等效模型的由来参数的物理意义简化的h参数等效模型 r b e {r\tiny be} rbe的近似表达式 共射放大电路动态参数的分析电压放大倍数 A ˙ u \.{A}\tiny u A˙u输入电阻 …

三种配置Spring程序的方法

1 使用XML文件配置Spring程序 在XML文件中使用bean标签&#xff0c;将其交给容器管理 class: 指定bean对应的类型的全限定名称id: 用于指定一个名称&#xff0c;作为该bean的唯一标识符&#xff0c;如果不需要id&#xff0c;也可不指定该属性name: 用于指定bean的别名&#x…

【小米电脑管家】安装使用教程--非小米电脑

安装说明功能体验下载资源 Xiaomi HyperOS发布后&#xff0c;小米妙享电脑端独立版本也走向终点&#xff0c;最新的【小米电脑管家】将会内置妙享实现万物互联。那么本篇文章将分享非小米电脑用户如何绕过设备识别验证安装使用【小米电脑管家】实现万物互联 安装说明 1.解压文…

如何用Python编写俄罗斯方块Tetris游戏?

在本文中&#xff0c;我们将用Python代码构建一个令人惊叹的项目&#xff1a;俄罗斯方块游戏。在这个项目中&#xff0c;我们将使用pygame库来构建游戏。要创建此项目&#xff0c;请确保您的系统中安装了最新版本的Python。让我们开始吧&#xff01; Pygame是一组跨平台的Pyth…

wireshark过滤包小技巧

1、过滤包含某个字符串的数据包&#xff1a; 或者&#xff1a; 2、过滤包含某一连续十六进制的数据包&#xff1a; 或者&#xff1a; 3、过滤精确到位数位置 或者&#xff1a;

关于使用EB tresos出现无法激活的情况解决

EB安装完成时需要激活才能使用的&#xff0c;不然都无法建立工程。 我在安装eb studio时就是在激活方面有问题导致无法使用&#xff0c;下面讲解出现了什么问题以及我如何去解除的。 1.出现的错误提示&#xff1f; ERROR&#xff1a;flexActAPPActivationSend按照在官网中&…