🍀 前言
博客地址:
👋 简介
Python3.7引入了dataclass。dataclass装饰器可以声明Python类为数据类;数据类适合用来存储数据,一般而言它具有如下特征:
- 数据类表示某种数据类型,数据对象代表一种特定类的实体,包含了实体的属性。
- 同类型的对象之间可以进行比较;如,大于、小于或等于。
📖 正文
1 普通类与数据类的区别
以前定义一个类,我们可以使用以下的方式
class Car():
def __init__(self, brand: str, price: float, color: str):
self.brand = brand
self.price = price
self.color = color
def __repr__(self):
return f"Car(brand='{self.brand}', price={self.price}, color='{self.color}')"
def __eq__(self, other):
return (self.brand, self.price, self.color) == (other.brand, other.price, other.color)
if __name__ == '__main__':
bmw = Car("宝马5系", 450000, "白色")
bmw2 = Car("宝马5系", 450000, "白色")
benz = Car("奔驰E级", 500000, "黑色")
print(bmw)
# Car(brand='宝马5系', price=450000, color='白色')
print(benz)
# Car(brand='奔驰E级', price=500000, color='黑色')
print(bmw == bmw2)
# True
现在通过@dataclass
装饰器来表示数据类,进行简化
from dataclasses import dataclass
@dataclass(order=True)
class Car():
brand: str
price: float
color: str
if __name__ == '__main__':
bmw = Car("宝马5系", 450000, "白色")
bmw2 = Car("宝马5系", 450000, "白色")
benz = Car("奔驰E级", 500000, "黑色")
print(bmw)
# Car(brand='宝马5系', price=450000, color='白色')
print(benz)
# Car(brand='奔驰E级', price=500000, color='黑色')
print(bmw == bmw2)
# True
2 dataclass详解
2.1 装饰器dataclass
dataclass完整形式为@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)
其中True为生成对应方法,False将不生成;若类中已定义对应方法,则忽略此参数)
init
:默认将生成__init__()
方法repr
:默认将生成__repr__()
方法;repr字符串包含类名、每个字段名称和其repr(按其类中定义顺序)eq
:默认将生成__eq__()
方法;如果传入False,那么__eq__()
方法将不会被dataclass添加,但会继承object.__eq__()
(比较id)order
:默认不生成__gt__()
、__ge__()
、__lt__()
、__le__()
方法unsafe_hash
:默认为False,则根据eq和frozen的设置方式生成__hash__()
方法- 如果eq和frozen都为真,默认情况会生成一个
__hash__()
方法 - 如果eq为真而frozen为假,则
__hash__()
将被设置为 None,将其标记为不可散列(确实如此,因为它是可变的 - 如果eq为假,则
__hash__()
将保持不变,这意味着将使用超类的__hash__()
方法(如果超类是object,将回退到基于id的散列)
- 如果eq和frozen都为真,默认情况会生成一个
frozen
:如果该属性为为true,则对象初始化后属性将无法修改
2.2 field
field(*,default=MISSING,default_factory=MISSING,init=True,repr=True,hash=None,compare=True, metadata=None)
default
:字段的默认值default_factory
:具有可变默认值的字段,必须是一个无参可调用对象;与default互斥(不可同时指定)init
:是否在__init__()方法中使用字段,默认值Truerepr
:是否在__repr__()方法中使用字段,默认值Truecompare
:是否在比较对象时, 包括该字段,默认值Truehash
:计算hash时, 是否包括字段,默认值Truemetadata
:包含字段信息的映射
from dataclasses import dataclass, field
@dataclass
class Tire:
"""
轮胎类
"""
number: int = 4
brand: str = '普利司通'
@dataclass(order=True)
class Car():
brand: str
price: float
color: str
# 引用默认长度 和 length: float = 5.10 等价
length: float = field(default=5.10)
# tire: str = Tire() 这种方式不起作用
tire: str = field(default_factory=lambda: Tire())
if __name__ == '__main__':
bmw = Car("宝马5系", 450000, "白色")
benz = Car("奔驰E级", 500000, "黑色")
print(bmw)
# Car(brand='宝马5系', price=450000, color='白色', length=5.1, tire=Tire(number=4, brand='普利司通'))
print(benz)
# Car(brand='奔驰E级', price=500000, color='黑色', length=5.1, tire=Tire(number=4, brand='普利司通'))
备注:
- length: float = field(default=5.10)默认值同样可以写成length: float = 5.10
- 默认值调用方法tire: str = field(default_factory=lambda: Tire()),tire: str = Tire() 这种方式不起作用
3 数据类的使用
3.1 不可变数据类型
首先我们将dataclass注解中frozen的属性的默认值False改为True
from dataclasses import dataclass
@dataclass(frozen=True)
class Student:
name: str
age: int
stu = Student("zhangsan", 18)
print(stu)
# Student(name='zhangsan', age=18)
然后我们修改年龄,结果发生报错
stu.age = 20
"""
Traceback (most recent call last):
File "D:\01 programming\09python\python-tools\test\demo01\python数据类dataclass.py", line 70, in <module>
stu.age = 20
File "<string>", line 3, in __setattr__
dataclasses.FrozenInstanceError: cannot assign to field 'age'
"""
3.2 数据比较
首先dataclass注解中order的值改为True,表示开启__eq__(),lt()等方法
然后修改field的compare属性的值为False,关闭对name属性的比较
from dataclasses import dataclass, field
@dataclass(order=True)
class Student:
name: str = field(compare=False)
age: int
stus = [Student('zhangsan', 20), Student('lisi', 21), Student('wangwu', 19)]
# 根据学生年龄排序
print(sorted(stus))
# [Student(name='wangwu', age=19), Student(name='zhangsan', age=20), Student(name='lisi', age=21)]
3.3 后处理
通过__post_init__
可做后处理(在__init__
返回前,自动调用)
首先将field中init属性改为False,表示该字段属性不进行初始化的操作
然后在__post_init__中,对定义的info属性赋值,进行初始化的后处理
from dataclasses import dataclass, field
@dataclass
class Student:
name: str
age: int = 0
# 个人信息
info: str = field(init=False)
def __post_init__(self):
self.info = f'我的名字叫{self.name}, 今年{self.age}岁了'
stu = Student("张三", 20)
print(stu)
# Student(name='张三', age=20, info='我的名字叫张三, 今年20岁了')