Dataclasses 使用Demo

文章目录
  1. 1. 导入库
  2. 2. 原始实现
  3. 3. dataclasses

dataclasses会简化数据类的编码,period。在Python3.7中引入。

导入库

1
2
3
4
5
6
7
# https://www.youtube.com/watch?v=vBH6GRJ1REM
import dataclasses
import inspect
from dataclasses import dataclass, field
from pprint import pprint
from typing import List
import attr

原始实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class ManualComment:
def __init__(self, id: int, text: str):
self.id: int = id
self.text: str = text

def __repr__(self):
return "{}(id={}, text={})".format(self.__class__.__name__, self.id, self.text)

def __eq__(self, other):
if other.__class__ is self.__class__:
return (self.id, self.text) == (other.id, other.text)
else:
return NotImplemented

def __ne__(self, other):
result = self.__eq__(other)
if result is NotImplemented:
return NotImplemented
else:
return not result

def __hash__(self):
return hash((self.__class__, self.id, self.text))

def __lt__(self, other):
if other.__class__ is self.__class__: #类型安全,没有比较不同的类
return (self.id, self.text) < (other.id, other.text)
else:
return NotImplemented

def __le__(self, other):
if other.__class__ is self.__class__:
return (self.id, self.text) <= (other.id, other.text)
else:
return NotImplemented

def __gt__(self, other):
if other.__class__ is self.__class__:
return (self.id, self.text) > (other.id, other.text)
else:
return NotImplemented

def __ge__(self, other):
if other.__class__ is self.__class__:
return (self.id, self.text) >= (other.id, other.text)
else:
return NotImplemented

可能会将上面这个类放在字典中,因此要实现__hash__方法;同时既然可哈了,它的内容最好不可变:通过将数据成员前面添加两个下划线self.__id

同时为了支持比较,也有必要实现__le__& __gt__等方法;functools 提供total_ordering 装饰器,可以只实现__lt__,其会自动补全剩下的方法。但是会比较慢,因此自己实现会比较好。

  • ManualComment如果再增加一个数据成员 author,需要改动哪些地方? 太复杂了。

dataclasses

  1. 直接指明有哪些数据成员.通过field函数可以指定成员如何初始化、参与哪些行为, etc
  2. 可为dataclasses.dataclass decorator传参的方式指定类有哪些行为。是否可改变(frozen=True),是否支持排序(order=True)
1
2
3
4
5
6
7
8
9
# frozen = True, immutable
# order = True,

@dataclass(frozen=True, order=True)
class Comment:
id: int
text: str = "" # feild(default='')
replies: List[int] = field(default_factory=list, repr=False, compare=False) # mutable default value?
#compare=False, 不参与比较
1
2
comment = Comment(1, "I just subscribed!")
print(comment)
Comment(id=1, text='I just subscribed!')
1
2
# frozen=True
comment.id = 3 # can't immutable
---------------------------------------------------------------------------

FrozenInstanceError                       Traceback (most recent call last)

<ipython-input-12-429f24a37a2f> in <module>
----> 1 comment.id = 3


<string> in __setattr__(self, name, value)


FrozenInstanceError: cannot assign to field 'id'
1
2
3
c1 = Comment(20, 'hello')
c2 = Comment(30, 'jack')
c1 < c2
True
  • default_factory=list #参数的默认值不能是可变类型,否则会因为多个instance共享一个实例产生难以追踪的错误。因此采用构造工厂的方式,每次初始化都新建一个list
1
dataclasses.astuple(comment), dataclasses.asdict(comment)
((1, 'I just subscribed!', []),
 {'id': 1, 'text': 'I just subscribed!', 'replies': []})
1
inspect.getmembers(Comment, inspect.isfunction)
[('__delattr__',
  <function __main__.__create_fn__.<locals>.__delattr__(self, name)>),
 ('__eq__', <function __main__.__create_fn__.<locals>.__eq__(self, other)>),
 ('__ge__', <function __main__.__create_fn__.<locals>.__ge__(self, other)>),
 ('__gt__', <function __main__.__create_fn__.<locals>.__gt__(self, other)>),
 ('__hash__', <function __main__.__create_fn__.<locals>.__hash__(self)>),
 ('__init__',
  <function __main__.__create_fn__.<locals>.__init__(self, id: int, text: str = '', replies: List[int] = <factory>) -> None>),
 ('__le__', <function __main__.__create_fn__.<locals>.__le__(self, other)>),
 ('__lt__', <function __main__.__create_fn__.<locals>.__lt__(self, other)>),
 ('__repr__', <function __main__.__create_fn__.<locals>.__repr__(self)>),
 ('__setattr__',
  <function __main__.__create_fn__.<locals>.__setattr__(self, name, value)>)]