掌握python的dataclass,让你的代码更简洁优雅

打印 上一主题 下一主题

主题 860|帖子 860|积分 2580

dataclass是从Python3.7版本开始,作为标准库中的模块被引入。
随着Python版本的不绝更新,dataclass也逐步发展和完善,为Python开辟者提供了更加便捷的数据类创建和管理方式。
dataclass的主要功能在于帮助我们简化数据类的界说过程。
本文总结了几个我平时使用较多dataclass本领。
1. 传统的类界说方式

首先,从平时量化分析的场景中简化一个关于 币买卖业务 的类用来演示。
简化之后,这里只保存5个字段,分别是买卖业务ID买卖业务对代价是否成功参与买卖业务的地址列表
  1. class CoinTrans:
  2.     def __init__(
  3.         self,
  4.         id: str,
  5.         symbol: str,
  6.         price: float,
  7.         is_success: bool,
  8.         addrs: list,
  9.     ) -> None:
  10.         self.id = id
  11.         self.symbol = symbol
  12.         self.price = price
  13.         self.addrs = addrs
  14.         self.is_success = is_success
复制代码
Python传统界说类的方式,如上通过__init__函数来初始化对象的各个属性。
通过这个类构造对象并打印:
  1. if __name__ == "__main__":
  2.     coin_trans = CoinTrans("id01", "BTC/USDT", "71000", True, ["0x1111", "0x2222"])
  3.     print(coin_trans)
复制代码
运行结果:
  1. [/code]这里只是打印出对象的地址,并没有按照我们渴望的那样打印对象各个属性的值。
  2. 传统的类中,我们如果希望打印出可读的结果,需要自己去实现__str__函数。
  3. [code]# 在上面的 CoinTrans 类中添加下面的方法
  4. def __str__(self) -> str:
  5.     return f"交易信息:{self.id}, {self.symbol}, {self.price}, {self.addrs}, {self.is_success}"
复制代码
再次运行,结果如下:
  1. 交易信息:id01, BTC/USDT, 71000, ['0x1111', '0x2222'], True
复制代码
2. dataclass装饰器界说类

下面看看使用dataclass装饰器来界说上面同样的类有多简单。
  1. from dataclasses import dataclass
  2. @dataclass
  3. class CoinTrans:
  4.     id: str
  5.     symbol: str
  6.     price: float
  7.     is_success: bool
  8.     addrs: list
复制代码
再次运行:
  1. if __name__ == "__main__":
  2.     coin_trans = CoinTrans("id01", "BTC/USDT", "71000", True, ["0x1111", "0x2222"])
  3.     print(coin_trans)
复制代码
得到如下结果:
  1. CoinTrans(id='id01', symbol='BTC/USDT', price='71000', is_success=True, addrs=['0x1111', '0x2222'])
复制代码
不需要__init__,也不需要__str__,只要通过 @dataclass装饰之后,就可以打印出对象的详细内容。
2.1. 默认值

dataclass装饰器的方式来界说类,设置默认值很简单,直接在界说属性时就可以设置。
  1. @dataclass
  2. class CoinTrans:
  3.     id: str = "id01"
  4.     symbol: str = "BTC/USDT"
  5.     price: float = "71000.8"
  6.     is_success: bool = True
  7.     addrs: list[str] = ["0x1111", "0x2222"]
  8. if __name__ == "__main__":
  9.     coin_trans = CoinTrans()
  10.     print(coin_trans)
复制代码
运行之后发现,在addrs属性那行会报错:
  1. ValueError: mutable default <class 'list'> for field addrs is not allowed: use default_factory
复制代码
大概的意思就是,list作为一种可变的类型(引用类型,会有被其他对象不测修改的风险),不能直接作为默认值,需要用工厂方法来产生默认值。
其他字符串,数值,布尔类型的数据则没有这个问题。
我们只要界说个函数来产生此默认值即可。
  1. def gen_list():
  2.     return ["0x1111", "0x2222"]
  3. @dataclass
  4. class CoinTrans:
  5.     id: str = "id01"
  6.     symbol: str = "BTC/USDT"
  7.     price: float = "71000.8"
  8.     is_success: bool = True
  9.     addrs: list[str] = field(default_factory=gen_list)
  10. if __name__ == "__main__":
  11.     coin_trans = CoinTrans()
  12.     print(coin_trans)
复制代码
再次运行,可以正常执行:
  1. CoinTrans(id='id01', symbol='BTC/USDT', price='71000.8', is_success=True, addrs=['0x1111', '0x2222']
复制代码
2.2. 隐蔽敏感信息

我们打印对象信息的时候,有时执行打印此中几个属性的信息,涉及敏感信息的属性不希望打印出来。
比如,上面的对象,如果不想打印出is_success和addrs的信息,可以设置repr=False。
  1. @dataclass
  2. class CoinTrans:
  3.     id: str = "id01"
  4.     symbol: str = "BTC/USDT"
  5.     price: float = "71000.8"
  6.     is_success: bool = field(default=True, repr=False)
  7.     addrs: list[str] = field(default_factory=gen_list, repr=False)
复制代码
再次运行后显示:
  1. CoinTrans(id='id01', symbol='BTC/USDT', price='71000.8')
复制代码
2.3. 只读对象

数据分析时,大部分下情况下,原始数据读取之后是不能修改的。
这种情况下,我们可以用dataclass的frozen属性来设置数据类只读,防止不小心窜改了数据。
未设置frozen属性之前,可以随意修改对象的属性,比如:
  1. if __name__ == "__main__":
  2.     coin_trans = CoinTrans()
  3.     print(f"修改前: {coin_trans}")
  4.     coin_trans.symbol = "ETH/USDT"
  5.     print(f"修改后: {coin_trans}")
复制代码
运行结果:
  1. 修改前: CoinTrans(id='id01', symbol='BTC/USDT', price='71000.8')
  2. 修改后: CoinTrans(id='id01', symbol='ETH/USDT', price='71000.8')
复制代码
设置frozen属性之后,看看修改属性值会怎么样:
  1. @dataclass(frozen=True)
  2. class CoinTrans:
  3.     id: str = "id01"
  4.     #... 省略 ...
复制代码
再次运行,会发现修改属性会触发异常。
  1. 修改前: CoinTrans(id='id01', symbol='BTC/USDT', price='71000.8')
  2. Traceback (most recent call last):
  3.   File "D:\projects\python\samples\data_classes\main.py", line 66, in <module>
  4.     coin_trans.symbol = "ETH/USDT"
  5.     ^^^^^^^^^^^^^^^^^
  6.   File "<string>", line 4, in __setattr__
  7. dataclasses.FrozenInstanceError: cannot assign to field 'symbol'
复制代码
2.4. 转化为元组和字典

最后,dataclasses模块还提供了两个函数可以很方便的将数据类转换为元组字典
这在和其他分析步伐交互时非常有用,由于和其他步伐交互时,参数一般都用元组或者字典这种简单通用的结构,
而不会直接用自己界说的数据类。
  1. from dataclasses import dataclass, field, astuple, asdict
  2. if __name__ == "__main__":
  3.     coin_trans = CoinTrans()
  4.     print(astuple(coin_trans))
  5.     print(asdict(coin_trans))
复制代码
运行结果:
  1. ('id01', 'BTC/USDT', '71000.8', True, ['0x1111', '0x2222'])
  2. {'id': 'id01', 'symbol': 'BTC/USDT', 'price': '71000.8', 'is_success': True, 'addrs': ['0x1111', '0x2222']}
复制代码
3. 总结

在Python中,数据类主要用于存储数据,并通常包含属性和方法来操作这些数据。
然而,在界说数据类时,我们通常需要编写一些重复性的代码,如构造函数、属性访问器和字符串表示等。
dataclass装饰器的出现,使得这些通用方法的生成变得自动化,从而极大地简化了数据类的界说过程。
总的来说,dataclass通过简化数据类的创建和管理过程,进步了开辟效率,是我们在数据分析时的一个非常有用的工具。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

何小豆儿在此

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表