定义
dataclass 是python 3.7新增的一个模块,适用于存储数据对象
数据对象特性:
1、存储数据并代表某种数据类型
2、可以与同一类型的其他对象比较
用法
提供了一个装饰器dataclass,用于将类转换为dataclass
下边是dataclass带来的变化,分为初始化、表示、数据比较、可调用的装饰、Frozen(不可变)实例、后期初始化处理、继承
初始化
from dataclasses import dataclass
# 初始化
# 未使用dataclass
class Number:
def __init__(self, val):
self.val = val
one = Number(1)
print(one.val) # 1
# 使用dataclass
# 可以设置默认值val:int = 0
@dataclass
class Number:
val:int
one = Number(1)
print(one.val) # 1
说明:
dataclass带来的变化:
1、无需定义__init__,然后将值赋给self
2、以更加易读的方式定义了成员属性及类型提示
表示
# 表示
# 未使用dataclass
class Number:
def __init__(self, val):
self.val = val
one = Number(1)
print(one) # <__main__.Number object at 0x000000000216C2B0>
# 可在类中定义一个__repr__方法来表示
class Number:
def __init__(self, val):
self.val = val
def __repr__(self):
return str(self.val)
one = Number(1)
print(one) # 1
# 使用dataclass
@dataclass
class Number:
val:int = 0
one = Number(1)
print(one) # Number(val=1)
说明:
dataclass 会自动添加一个 __repr__ 函数,这样我们就不必手动实现它
数据比较
# 数据比较
# 未使用dataclass
class Number:
def __init__(self, val):
self.val = val
def __eq__(self, other):
return self.val == other.val
def __lt__(self, other):
return self.val < other.val
# 使用dataclass
@dataclass(order = True)
class Number:
val:int = 0
import random
a = [Number(random.randint(1,10)) for _ in range(10)]
print(a) # [Number(val=3), Number(val=5), Number(val=5), Number(val=4), Number(val=1), Number(val=5), Number(val=10), Number(val=8), Number(val=1), Number(val=5)]
sorted_a = sorted(a)
print(sorted_a) # [Number(val=1), Number(val=1), Number(val=3), Number(val=4), Number(val=5), Number(val=5), Number(val=5), Number(val=5), Number(val=8), Number(val=10)]
reverse_sorted_a = sorted(a, reverse= True)
print(reverse_sorted_a) # [Number(val=10), Number(val=8), Number(val=5), Number(val=5), Number(val=5), Number(val=5), Number(val=4), Number(val=3), Number(val=1), Number(val=1)]
说明:
- 不需要定义 __eq__ 和 __lt__ 方法,因为当 order = True 被调用时,dataclass 装饰器会自动将它们添加到我们的类定义
- 自动生成的方法属性顺序与你在dataclass类中定义的顺序一致
- 内置的sorted函数依赖于比较两个对象
可调用的装饰
@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)
class C:
…
说明:
1.参数
init:默认将生成 __init__ 方法。如果传入 False,那么该类将不会有 __init__ 方法。
repr:__repr__ 方法默认生成。如果传入 False,那么该类将不会有 __repr__ 方法。
eq:默认将生成 __eq__ 方法。如果传入 False,那么 __eq__ 方法将不会被 dataclass 添加,但默认为 object.__eq__。
order:默认将生成 __gt__、__ge__、__lt__、__le__ 方法。如果传入 False,则省略它们。
2.可以有选择性地定义所dunder(双下划线方法,即魔法方法)
修改默认生成的函数:@dataclass(repr = False)
Frozen(不可变)实例
# 不可变实例
# 使用dataclass
@dataclass(frozen=True)
class Number:
val:int = 0
one = Number(1)
print(one.val) # 1
# one.val = 2 # 报错'Number' object attribute 'val' is read-only
说明:
frozen实例不可变的特性可以用来存储常数、设置
后期初始化处理
# 后期初始化处理
# 未使用 dataclass
import math
class Float:
def __init__(self, val = 0):
self.val = val
self.process()
def process(self):
self.decimal, self.integer = math.modf(self.val)
a = Float(2.2)
print(a.decimal) # 0.20000000000000018
print(a.integer) # 2.0
# 使用 dataclass
import math
@dataclass
class FloatNumber:
val:float = 0.0
def __post_init__(self):
self.decimal, self.integer = math.modf(self.val)
a = FloatNumber(2.2)
print(a.decimal) # 0.20000000000000018
print(a.integer) # 2.0
说明:
__init__方法初始化失去了在变量被赋值之后立即需要的函数调用或处理的灵活性。post_init可以弥补这种场景的缺陷,处理后期初始化操作
继承
# 继承
# 父类中定义的属性将在子类中可用
@dataclass
class Person:
age: int = 0
name: str = '1'
@dataclass
class Student(Person):
grade: int = 0
s = Student(18, "mingliang", 100)
print(s.age)
print(s.name)
print(s.grade)
# 继承过程中__post_init__的行为
@dataclass
class A:
a: int
def __post_init__(self):
print('A')
@dataclass
class B(A):
b: int
def __post_init__(self):
print('B')
a = B(1, 2) # B 只有 B的 __post_init__ 被调用
@dataclass
class B(A):
b: int
def __post_init__(self):
super().__post_init__() # 父类的函数可以使用 super 调用
print('B')
a = B(1, 2) # A B
说明:
- 父类中定义的属性将在子类中可用
- 父类的函数可以使用 super 调用