python3.7 的dataclass新特性大大简化了定义类对象的代码量,代码简洁明晰。通过使用装饰器来修饰类的设计,例如
运行结果
而常规的类,按照3.7之前的语法类似于这样
虽然这种写法并没有使用更多的代码量,但是我们很容易看到为了初始化,仅仅只是为了初始化一个对象,rank和suit已经重复了三次。此外,如果你试图使用这个RegularCard类,你会注意到对象的表示不是很具描述性,并且已有的类与新声明的类是无法比较是否相同的。
具体请看下面代码
运行结果
dataclass还在底层给我们做了更多的有用的封装。默认情况下dataclass实现了方法,可以很好的提供字符串表示;也是了方法,可以做基本的对象比较。而如果RegularCard想实现上面的功能需要写大量的声明,代码量多的吓人
在本教程中,您将准确了解dataclass类所带来的便利性。除了很好的表示(当使用print时可以在很好的打印出来)和比较(是否相同),你会看到:
如何给dataclass对象添加默认的字段(field)
如何让dataclass允许对象进行排序
如何让dataclass表示不可更改数据
如何让dataclass处理继承
dataclass类的替代方案
可能你接触过nametuple,它常常用来创造可读的轻量级数据结构。实际上我们可以通过nametuple重复创造数据实例:
NamedTupleCard可以做到DataClassCard能过的事情:
运行结果跟 DataClassCard一样
但是nametuple也有一些限制和不足。例如,我们不能对nametuple实例的属性值进行更改,因为从根本上将nametuple是元组类,是不可更改数据类型。在某些应用中,这可能是很棒的功能,但在其他应用场景中,拥有更多灵活性会更好:
由于nametuple不可更改性,运行结果报错如下
dataclass基础
现在返回到dataclass,我们要创建一个Position类,包含名字和经纬度信息的地理位置信息类:
我们解读下Position类代码含义。首先使用放在Position上方起到装饰器语法作用。
通过装饰后的Position,我们可以给Position增加一些默认的字段,并且声明这些字段的类型。
运行结果如下:
我们也可以使用类似于nametuple语法的make_dataclass来创建Position类。代码如下
dataclass实际上也是普通的python对象,只不过dataclass帮我们将 封装,更简洁的提供给我们使用。
dataclass类的默认属性值
在dataclass中很方便的给属性值添加默认值
dataclass默认值设置类似于方法
默认经纬度均为0.运行结果如下
稍后我们会讲到默认工厂(default factory),从而为我们默认值设置提供了更多更复杂的功能。
类型提示(Type Hints)
您可能已经注意到我们使用类型提示定义的字段:表示名称应该是文本字符串(str类型)。
实际上,在数据类中定义字段时,必须添加某种类型提示。 如果没有类型提示,该字段将不是dataclass类的一部分。 但是,如果您不想向dataclass类添加显式类型,请使用typing.Any:
虽然在使用数据类时需要以某种形式添加类型提示,但这些类型在运行时不会强制执行。
上面的代码运行没有任何问题,运行结果。我们发现name是任意类型,而values也是任意类型,虽然默认设置为整数42,但是在这里我们输入的是字符串29,也能正常运行。
添加方法
dataclass类就是普通的python类,所以我们可以像给类定义方法一样给dataclass类定义方法。
这里我们定义距离计算方法,为了方便演示,我们这里假设地球是二维平面,经纬度代表坐标轴中的位置,使用欧几里得方法计算距离即可。
A点是坐标原点,B点(0, 2), C点(3, 4)。运行结果
灵活的dataclass
到现在位置,我们已经了解了dataclass的基本特性,现在我们接触些dataclass的高级用法,如参数和field()函数。将这两者结合能让我们更方便的 控制我们创造的类。
让我们回到您在本教程开头看到的扑克牌示例,并在我们处理时添加一个包含一副牌的类:
上面的two_cards是最简单的一副牌(Deck类),运行结果如下
往期文章昨日财报
赞赏、点赞、转发、AD支持都是对大邓的认可和支持,希望大家在阅读后顺便帮大邓转发一下。额,昨天仅有0.14RMB收入!