创建你的第一个数据类

让我们创建一个数据类,该数据类表示3D坐标系中的一个点。

@dataclass装饰器用于创建数据类。 x,y和z是我们数据类中的字段。 注意,你要用类型注释来指定字段的数据类型。记住,类型注释不是静态类型声明,这意味着有人可以为x,y或z字段传递除int之外的任何数据类型。

from dataclasses import dataclass@dataclassclass Coordinate: x: int y: int z: int
默认情况下,数据类随附实现了__init __,__ repr__和__eq__方法,我们不需要自己来实现这些方法。
但是__init__,__repr__和__eq__没有在Coordinate类中实现,因为有了数据类,我们可以通过使用这些方法,来大量节省时间。from dataclasses import dataclass@dataclassclass Coordinate: x: int y: int z: inta = Coordinate(4, 5, 3)print(a) # 输出:坐标(x = 4,y = 5,z = 3)
字段的默认值
我们可以为字段分配默认值。 在以下示例中, 从pi字段可以看到,我们可以为数据类中的字段分配默认值。
from dataclasses import dataclass@dataclassclass CircleArea: r: int pi: float = 3.14 @property def area(self): return self.pi * (self.r ** 2)a = CircleArea(2)print(repr(a)) # 输出:CircleArea(r = 2,pi = 3.14)print(a.area) # 输出: 12.56
自定义字段和数据类
通过使用数据类装饰器或字段函数的参数,可以自定义字段和数据类。我将在接下来的示例中自定义任何内容时进行说明,并在本文结尾给出字段和数据类的所有参数。
数据类是可变的还是不可变的?
默认情况下,数据类是可变的,这意味着我们可以为字段赋值。但是,通过将冻结参数设置为True,仍然可以使其不可变。
可变示例from dataclasses import dataclass@dataclassclass CircleArea: r: int pi: float = 3.14 @property def area(self): return self.pi * (self.r ** 2)a = CircleArea(2)a.r = 5print(repr(a)) # 输出: CircleArea(r=5, pi=3.14)print(a.area) # 输出: 78.5
不可变示例
当我们将Frozen设置为True时,我们将无法再为字段分配值。 我们可以在以下示例中看到异常输出。
from dataclasses import dataclass@dataclass(frozen=True)class CircleArea: r: int pi: float = 3.14 @property def area(self): return self.pi * (self.r ** 2)a = CircleArea(2)a.r = 5# 发生异常:dataclasses.FrozenInstanceError数据类:# 无法分配给字段“ r”
比较数据类
假设你想创建一个表示向量的数据类,并想要对它们进行比较。你会怎么做?
默认情况下,数据类的order参数为False。当你将其设置为True时,将自动为你的数据类生成方法。因此,你可以将对象作为其字段的元组顺序进行比较。
让我们来研究下面的这个例子。我们可以比较v2和v1,因为我们将order设置为True。但这里有一个问题,那就是比较的逻辑。当你说v2>v1时,它比较这两个向量,比如(8,15)>(7,20)。所以v2>v1的输出是真的。
回想一下,元组比较是逐位置进行的。它首先比较8到7,结果为真,然后比较结果为真。如果它们相等,则比较15>20,则结果为假。from dataclasses import dataclass, field@dataclass(order=True)class Vector: x: int y: intv1 = Vector(8, 15)v2 = Vector(7, 20)print(v2 > v1)
在回顾元组是如何比较的之后,很明显这种比较没有任何意义。我想用向量的大小来比较它们。这里的问题是,在创建每个实例时,你不想去计算向量的大小。
在这种情况下,我们将受益于field函数和__post_init__方法。field函数将帮助我们自定义幅度场。__post_init__方法将帮助我们在初始化后确定该向量的大小。
我们通过使用数据类中的字段函数来自定义幅度字段。 通过将init设置为False,基本上我们不需要__init__方法中的幅度参数。 因为我们要在初始化后使用__post_init__方法确定其值
from dataclasses import dataclass, field@dataclass(order=True)class Vector: magnitude: float = field(init=False) x: int y: int def __post_init__(self): self.magnitude = (self.x ** 2 + self.y ** 2) ** 0.5v1 = Vector(9, 12)print(v1) # 输出:矢量(幅值=15.0,x=9,y=12)v2 = Vector(8, 15)print(v2) # output: Vector(magnitude=17.0, x=8, y=15)print(v2 > v1) # output: True
转换为字典或元组
你可以在元组或字典中获取数据类的属性。 你所需要做的就是从数据类中导入asdict和astuple函数。from dataclasses import dataclass, asdict, astuple@dataclassclass Vector: x: int y: int z: intv = Vector(4, 5, 7)print(asdict(v)) # 输出: {'x': 4, 'y': 5, 'z': 7}print(astuple(v)) # 输出: (4, 5, 7)
继承
你可以像Python中的普通类那样对数据类进行子类化。
from dataclasses import dataclass@dataclassclass Employee: name: str lang: str@dataclassclass Developer(Employee): salary: intHalil = Developer('Halil', 'Python', 5000)print(Halil) # 输出:开发人员(名称='Halil',lang ='Python',月薪= 5000)
使用继承时有一个常见错误。 默认情况下,将lang字段设置为Python时,必须为lang字段之后的字段提供默认值。from dataclasses import dataclass@dataclassclass Employee: name: str lang: str = 'Python'@dataclassclass Developer(Employee): salary: intHalil = Developer('Halil', 'Python', 5000)#输出:TypeError:非默认参数'salary'跟随默认参数
为了了解原因,让我们看看我们的初始化方法是什么样子的。回想一下,具有默认值的参数应该在没有默认值的参数之后。
def __init__(name: str, lang: str = 'Python', salary: int): ...
我们可以通过为salary字段提供默认值来修复它。from dataclasses import dataclass@dataclassclass Employee: name: str lang: str = 'Python'@dataclassclass Developer(Employee): salary: int = 0Halil = Developer('Halil', 'Python', 5000)print(Halil) # 输出:开发人员(名称='Halil',lang ='Python',工资= 5000)
从插入的数据类中获益
默认情况下,属性存储在字典中。我们可以从插入的数据类中获得更快的属性,访问和使用更少的内存。
from dataclasses import dataclass@dataclassclass Employee: name: str lang: strHalil = Employee('Halil', 'Python')print(Halil.__dict__) # name': 'Halil', 'lang': 'Python'}
我们可以使用插入数据类来使用更少的内存并更快地访问数据类属性。from dataclasses import dataclass@dataclassclass Employee: __slots__ = ('name', 'lang') name: str lang: strHalil = Employee('Halil', 'Python')
数据类别参数
我们更改了数据类修饰器中的某些参数以自定义数据类。以下为参数列表:init:如果为True,则会在你的数据类中生成__init__方法。(默认为True)
repr:如果为True,则会在你的数据类中生成__repr__方法。(默认为True)
eq:如果为True,则会在你的数据类中生成__eq__方法。(默认为True)
顺序:如果为True,则会在你的数据类中生成__lt __,__ le __,__ gt__和__ge__方法。(默认为False)
unsafe_hash:如果为True,则会在你的数据类中生成__hash__方法。(默认为False)
冻结:如果为True,则不能分配给字段。(默认为False。)
请注意,如果order为True,则eq必须为True,否则将引发ValueError异常。
字段参数init:如果为True,则此字段包含在生成的__init__方法中。(默认为True)
repr:如果为True,则此字段包含在生成的__repr__方法中。(默认为True)
compare:如果为True,则此字段包含在生成的比较和相等方法中。(默认为True)
哈希:如果为True,则此字段包含在生成的__hash__方法中。(默认为无)
默认值:这将是此字段的默认值(如果提供)。
default_factory:如果提供,则必须为零参数可调用对象,当此字段需要默认值时将调用该参数。
元数据:这可以是映射,也可以是无,将其视为空字典。

结论

以上所有内容是对数据类的简要介绍,如果你还在嫌弃Python运行起来很慢,很占内存,那不妨试试用数据类来解决这些问题。希望这些内容对各位同学有帮助哈!