dataclasses会简化数据类的编码,period。在Python3.7中引入。
导入库 1 2 3 4 5 6 7 import dataclassesimport inspectfrom dataclasses import dataclass, fieldfrom pprint import pprintfrom typing import Listimport 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
直接指明有哪些数据成员.通过field函数可以指定成员如何初始化、参与哪些行为, etc
可为dataclasses.dataclass
decorator传参的方式指定类有哪些行为。是否可改变(frozen=True),是否支持排序(order=True)
1 2 3 4 5 6 7 8 9 @dataclass(frozen=True, order=True) class Comment : id: int text: str = "" replies: List[int] = field(default_factory=list, repr=False , compare=False )
1 2 comment = Comment(1 , "I just subscribed!" ) print(comment)
Comment(id=1, text='I just subscribed!')
---------------------------------------------------------------------------
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)>)]