定义一个dataclass
深入dataclass装饰器
数据类的基石——dataclasses.field
一些常用函数
dataclass继承
总结
dataclass简介dataclass的定义位于PEP-557,根据定义一个dataclass是指“一个带有默认值的可变的namedtuple”,广义的定义就是有一个类,它的属性均可公开访问,可以带有默认值并能被修改,而且类中含有与这些属性相关的类方法,那么这个类就可以称为dataclass,再通俗点讲,dataclass就是一个含有数据及操作数据方法的容器。
乍一看可能会觉得这个概念不就是普通的class么,然而还是有几处不同:
相比普通class,dataclass通常不包含私有属性,数据可以直接访问
dataclass的repr方法通常有固定格式,会打印出类型名以及属性名和它的值
dataclass拥有__eq__和__hash__魔法方法
dataclass有着模式单一固定的构造方式,或是需要重载运算符,而普通class通常无需这些工作
基于上述原因,通常自己实现一个dataclass是繁琐而无聊的,而dataclass单一固定的行为正适合程序为我们自动生成,于是dataclasses模块诞生了。
配合类型注解语法,我们可以轻松生成一个实现了__init__,__repr__,__cmp__等方法的dataclass:
from dataclasses import dataclass @dataclass class InventoryItem: '''Class for keeping track of an item in inventory.''' name: str unit_price: float quantity_on_hand: int = 0 def total_cost(self) -> float: return self.unit_price * self.quantity_on_hand同时使用dataclass也有一些好处,它比namedtuple更灵活。同时因为它是一个常规的类,所以你可以享受继承带来的便利。
dataclass的使用我们分x步介绍dataclass的使用,首先是如何定义一个dataclass。
定义一个dataclassdataclasses模块提供了一个装饰器帮助我们定义自己的数据类:
@dataclass class Lang: """a dataclass that describes a programming language""" name: str = 'python' strong_type: bool = True static_type: bool = False age: int = 28我们定义了一个描述某种程序语言特性的数据类——Lang,在接下来的例子中我们都会用到这个类。
在数据类被定义后,会根据给出的类型注解生成一个如下的初始函数:
def __init__(self, name: str='python', strong_type: bool=True, static_type: bool=False, age: int=28): self.name = name self.strong_type = strong_type self.static_type = static_type self.age = age可以看到初始化操作都已经自动生成了,让我们试用一下:
>>> Lang() Lang(name='python', strong_type=True, static_type=False, age=28) >>> Lang('js', False, False, 23) Lang(name='js', strong_type=False, static_type=False, age=23) >>> Lang('js', False, False, 23) == Lang() False >>> Lang('python', True, False, 28) == Lang() True例子中可以看出__repr__和__eq__方法也已经为我们生成了,如果没有其他特殊要求的话这个dataclass已经具备了投入生产环境的能力,是不是很神奇?
深入dataclass装饰器dataclass的魔力源泉都在dataclass这个装饰器中,如果想要完全掌控dataclass的话那么它是你必须了解的内容。
装饰器的原型如下:
dataclasses.dataclass(*, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)dataclass装饰器将根据类属性生成数据类和数据类需要的方法。
我们的关注点集中在它的kwargs上:
key含义init 指定是否自动生成__init__,如果已经有定义同名方法则忽略这个值,也就是指定为True也不会自动生成
repr 同init,指定是否自动生成__repr__;自动生成的打印格式为class_name(arrt1:value1, attr2:value2, ...)
eq 同init,指定是否生成__eq__;自动生成的方法将按属性在类内定义时的顺序逐个比较,全部的值相同才会返回True
order 自动生成__lt__,__le__,__gt__,__ge__,比较方式与eq相同;如果order指定为True而eq指定为False,将引发ValueError;如果已经定义同名函数,将引发TypeError
unsafehash 如果是False,将根据eq和frozen参数来生成__hash__:
1. eq和frozen都为True,__hash__将会生成
2. eq为True而frozen为False,__hash__被设为None
3. eq为False,frozen为True,__hash__将使用超类(object)的同名属性(通常就是基于对象id的hash)
当设置为True时将会根据类属性自动生成__hash__,然而这是不安全的,因为这些属性是默认可变的,这会导致hash的不一致,所以除非能保证对象属性不可随意改变,否则应该谨慎地设置该参数为True
frozen 设为True时对field赋值将会引发错误,对象将是不可变的,如果已经定义了__setattr__和__delattr__将会引发TypeError
有默认值的属性必须定义在没有默认值的属性之后,和对kw参数的要求一样。