Python 3.7 增加了一个标准库 dataclasses,里面有个装饰器叫 dataclass,非常实用,可以大大提升代码的可读性,最重要的是它让你少写很多代码,从而大大节省你的时间,今天就来说说为什么你需要 dataclass。
假如你正在为一个评论系统编写代码,你新建了一个类,定义了几个成员变量,并为其编写了 init、repr 等魔术方法,代码如下:
class Comment: 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)
为了不能发表重复的评论,你为此编写了 __eq__,__ne__,为了支持评论的排序,你还编写了 __lt__,__gt__,__le__,__ge__,为了让对象可以被 hash,你还增加了 __hash__,代码是越来越长,每个函数都用到了成员变量,如下:
class Comment: 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
现在,你突然想起还要加一个字段,就是评论者的 id:author_id,然后你不得不在每个函数里面都手动添加上这个字段,很麻烦,工作量相当于重写一个类了,如果不小心哪一个忘记添加了,这就是一个有 bug 的类。
问题是,后面还有可能增加字段或删除字段,有没有办法在我定义好类的成员变量之后,这些方法去自动更新?省的我改来改去?
有,这就是今天的 dataclass,借助于 dataclass,只需要这样就可以了:
from dataclasses import dataclass @dataclass(frozen=True, order=True) class Comment: id: int text: str = ""
代码简洁了许多,且字段后面可以加上类型提示,来增加可读性。
如果要加一个字段 author_id,直接加就可以了,一点也不麻烦:
from dataclasses import dataclass @dataclass(frozen=True, order=True) class Comment: id: int author_id: int text: str = "" # 带有默认值的字段要放在后面
来验证一下:
import inspect from dataclasses import dataclass from pprint import pprint @dataclass(frozen=True, order=True) class Comment: id: int author_id: int text: str = "" # 带有默认值的字段要放在后面 def main(): comment = Comment(1,2,"I just subscribed!") print(comment) # frozen = True 表示这是不可变对象,初始化后不能重新赋值 # comment.id = 3 # can't immutable print(dataclasses.astuple(comment)) print(dataclasses.asdict(comment)) # 如果非要修改,可以这样 copy = dataclasses.replace(comment, id=3) print(copy) pprint(inspect.getmembers(Comment, inspect.isfunction)) if __name__ == '__main__': main()
运行结果如下所示:
从上面最后的结果可以看出,dataclass 自动给我们编写了很多魔术方法,省去了自己手动编写的麻烦。注意上述的 frozen = True 表示对象是不可变对象,初始化完成之后,不可对成员重新赋值,这一点可以应用在固定对象,不可变的配置信息等应用场景下,非常实用。
我们来看下官方文档的函数签名:
也就是说,默认情况下会为我们生成 __init__、__repr__、__eq__ 这样的魔术方法。对应的参数传入 True 或 False 来控制那些魔术方法是否自动生成,比如说:
- 如果传入 order = True,则会生成__lt__(), __le__(), __gt__(), __ge__()方法。
- 如果 eq 和 frozen 都是 True,则会生成 __hash__ 方法
如果你仍然要自己动手写这些函数也是可以的,比如说:当你自定义了 __init__()时,init = x 这个参数会被忽略。
不想全部的字段都参与?
看到这里,你已经知道 dataclass 能够自动生成<,=,>,<=和>=这些比较方法。但是这些比较方法的一个缺陷是,它们使用类中的所有字段进行比较,有没有办法让某些字段不参与比较呢?当然可以,比如说我们有一个 class,包含姓名、年龄、身高,我们不希望姓名参与比较,就可以这样写:
from dataclasses import dataclass,field @dataclass(order = True) class User: name: str = field(compare = False) age: int height: float
同样的,如果你不希望某个字段显示在 repr 中,那么可以可以指定 field(repr = False)。
以上就是“想更快的写完Python代码?dataclass 来帮你!”的详细内容,想要了解更多django框架内容欢迎持续关注编程学习网
扫码二维码 获取免费视频学习资料
- 本文固定链接: http://phpxs.com/post/9976/
- 转载请注明:转载必须在正文中标注并保留原文链接
- 扫码: 扫上方二维码获取免费视频资料