【Python】都2024了,还不会用dataclass,你Out了?

文摘   科技   2024-11-09 07:20   江苏  








01


引言





大家好,我是AI算法之道!


Python是我最喜欢的编程语言之一,它向来以其简单性、多功能性和可读性而闻名。


大家好!今天我想与大家分享Python3.7引入的标准库dataclass模块,其主要功能是帮助大家简化数据类的定义过程。


闲话少说,我们直接开始吧!





02


传统类的定义

首先,将一个与货币交易相关的类从通常的定量分析场景中简化出来,以进行演示。简化后,这里只保留了 5 个字段,即 id、symbol、price、is_success 和 addrs
class CoinTrans:    def __init__(        self,        id: str,        symbol: str,        price: float,        is_success: bool,        addrs: list,    ) -> None:        self.id = id        self.symbol = symbol        self.price = price        self.addrs = addrs        self.is_success = is_success
在 Python 中定义类的传统方法是通过 __init__ 函数初始化对象的各种属性。通过该类构建一个对象并打印出来:
if __name__ == "__main__":    coin_trans = CoinTrans("id01", "BTC/USDT", "71000", True, ["0x1111", "0x2222"])    print(coin_trans)

结果如下:

<__main__.CoinTrans object at 0x0000022A891FADD0>

在这里,我们只打印出了对象的地址,而不是像预期的那样打印每个对象的属性值。

在传统的类中,如果我们想打印可读的结果,就需要自己实现 __str__ 函数。

# Add the following method to the CoinTrans class above.def __str__(self) -> str:    return f"Transaction Information:{self.id}, {self.symbol}, {self.price}, {self.addrs}, {self.is_success}"

再次运行后,结果如下:

Trade information: id01, BTC/USDT, 71000, ['0x1111', '0x2222'], True




03


 使用dataclass定义类


让我们看看使用dataclass装饰器定义与上面相同的类有多简单。
from dataclasses import dataclass
@dataclassclass CoinTrans: id: str symbol: str price: float is_success: bool addrs: list
再跑一次,
if __name__ == "__main__":    coin_trans = CoinTrans("id01", "BTC/USDT", "71000", True, ["0x1111", "0x2222"])    print(coin_trans)
结果如下:
CoinTrans(id='id01', symbol='BTC/USDT', price='71000', is_success=True, addrs=['0x1111', '0x2222'])

无需__init__,无需__str__,只需用 @dataclass 装饰,就能打印出对象的具体内容。





04


 设置默认值


使用dataclass装饰器来定义类,可以非常简单地设置默认值,您可以在定义属性时直接设置默认值。

例如,例子为:

@dataclassclass CoinTrans:    id: str = "id01"    symbol: str = "BTC/USDT"    price: float = "71000.8"    is_success: bool = True    addrs: list[str] = ["0x1111", "0x2222"]
if __name__ == "__main__": coin_trans = CoinTrans() print(coin_trans)
运行后发现,在属性 addrs 所在的代码行中会出现错误:
ValueError: mutable default <class 'list'> for field addrs is not allowed: use default_factory

简单地说,其含义是作为可变类型(引用类型,有可能被其他对象无意修改)的列表不能直接用作默认值,而需要使用工厂方法生成。这个问题不适用于字符串、数字和布尔等其他数据类型。


为了解决上述问题,我们只需定义一个函数来生成默认值。

def gen_list():    return ["0x1111", "0x2222"]
@dataclassclass CoinTrans: id: str = "id01" symbol: str = "BTC/USDT" price: float = "71000.8" is_success: bool = True addrs: list[str] = field(default_factory=gen_list)
if __name__ == "__main__": coin_trans = CoinTrans() print(coin_trans)

再次运行,可以正常执行:

CoinTrans(id='id01', symbol='BTC/USDT', price='71000.8', is_success=True, addrs=['0x1111', '0x2222']






05


隐藏敏感信息


在打印对象信息时,有时我们只打印某些属性的信息,而不想打印敏感信息。例如,对于上述对象,如果不想打印 "is_success "和 "addrs "的信息,可以设置 repr=False。

比如:

@dataclassclass CoinTrans:    id: str = "id01"    symbol: str = "BTC/USDT"    price: float = "71000.8"    is_success: bool = field(default=True, repr=False)    addrs: list[str] = field(default_factory=gen_list, repr=False)

再次运行后显示:

CoinTrans(id='id01', symbol='BTC/USDT', price='71000.8')







06


只读对象


在分析数据时,大多数情况下,原始数据在读取后不能被修改。在这种情况下,我们可以使用冻结属性 dataclass 将数据类设置为只读,以防止数据被意外篡改。
在设置冻结属性之前,您可以自由修改对象属性,例如
if __name__ == "__main__":    coin_trans = CoinTrans()    print(f"Before modification: {coin_trans}")    coin_trans.symbol = "ETH/USDT"    print(f"after modification: {coin_trans}")

结果如下:

Original: CoinTrans(id='id01', symbol='BTC/USDT', price='71000.8')Modified: CoinTrans(id='id01', symbol='ETH/USDT', price='71000.8')


设置冻结属性后,看看修改属性值会发生什么:

@dataclass(frozen=True)class CoinTrans:    id: str = "id01"    #... omission ...

再次运行时,会发现修改属性会触发异常。

Before modification: CoinTrans(id='id01', symbol='BTC/USDT', price='71000.8')Traceback (most recent call last):  File "D:\projects\python\samples\data_classes\main.py", line 66, in <module>    coin_trans.symbol = "ETH/USDT"    ^^^^^^^^^^^^^^^^^  File "<string>", line 4, in __setattr__dataclasses.FrozenInstanceError: cannot assign to field 'symbol'



07


转换为元组或字典


最后,dataclass模块还提供了两个函数,可以方便地将数据类转换为元组和字典。这在与其他分析程序交互时非常有用,因为在与其他程序交互时,参数通常使用简单而常见的结构,如元组或字典,而不是直接使用自定义的数据类。
代码如下:
from dataclasses import dataclass, field, astuple, asdict
if __name__ == "__main__": coin_trans = CoinTrans() print(astuple(coin_trans)) print(asdict(coin_trans))
运行后结果如下:
('id01', 'BTC/USDT', '71000.8', True, ['0x1111', '0x2222']){'id': 'id01', 'symbol': 'BTC/USDT', 'price': '71000.8', 'is_success': True, 'addrs': ['0x1111', '0x2222']}






08


总结


在 Python 中,数据类主要用于存储数据,通常包含对这些数据进行操作的属性和方法。然而,在定义传统数据类时,我们经常需要编写一些重复的代码,如构造函数、属性访问函数和字符串表示法。


dataclass装饰器的出现自动生成了这些常用方法,大大简化了数据类的定义过程。


总之,简化dataclass 的创建和管理过程可以提高开发效率,是数据分析的一种有用工具。






点击上方小卡片关注我




添加个人微信,进专属粉丝群!


AI算法之道
一个专注于深度学习、计算机视觉和自动驾驶感知算法的公众号,涵盖视觉CV、神经网络、模式识别等方面,包括相应的硬件和软件配置,以及开源项目等。
 最新文章