python3.7标准库官方手册(Python3.7 dataclass使用指南小结)
python3.7标准库官方手册
Python3.7 dataclass使用指南小结dataclass简介
dataclass的定义位于PEP-557,根据定义一个dataclass是指“一个带有默认值的可变的namedtuple”,广义的定义就是有一个类,它的属性均可公开访问,可以带有默认值并能被修改,而且类中含有与这些属性相关的类方法,那么这个类就可以称为dataclass,再通俗点讲,dataclass就是一个含有数据及操作数据方法的容器。
乍一看可能会觉得这个概念不就是普通的class么,然而还是有几处不同:
1.相比普通class,dataclass通常不包含私有属性,数据可以直接访问
2.dataclass的repr方法通常有固定格式,会打印出类型名以及属性名和它的值
3.dataclass拥有__eq__和__hash__魔法方法
4.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。
定义一个dataclass
dataclasses模块提供了一个装饰器帮助我们定义自己的数据类:
|
@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参数的要求一样。
上面我们偶尔提到了field的概念,我们所说的数据类属性,数据属性实际上都是被field的对象,它代表着一个数据的实体和它的元信息,下面我们了解一下dataclasses.field。
数据类的基石——dataclasses.field
先看下field的原型:
|
dataclasses.field( * , default = MISSING, default_factory = MISSING, repr = True , hash = None , init = True , compare = True , metadata = None ) |
通常我们无需直接使用,装饰器会根据我们给出的类型注解自动生成field,但有时候我们也需要定制这一过程,这时dataclasses.field就显得格外有用了。
default和default_factory参数将会影响默认值的产生,它们的默认值都是None,意思是调用时如果为指定则产生一个为None的值。其中default是field的默认值,而default_factory控制如何产生值,它接收一个无参数或者全是默认参数的callable对象,然后用调用这个对象获得field的初始值,之后再将default(如果值不是MISSING)复制给callable返回的这个对象。
举个例子,对于list,当复制它时只是复制了一份引用,所以像dataclass里那样直接复制给实例的做法的危险而错误的,为了保证使用list时的安全性,应该这样做:
|
@dataclass class C: mylist: List [ int ] = field(default_factory = list ) |
当初始化C的实例时就会调用list()而不是直接复制一份list的引用:
|
>>> c1 = C() >>> c1.mylist + = [ 1 , 2 , 3 ] >>> c1.mylist [ 1 , 2 , 3 ] >>> c2 = C() >>> c2.mylist [] |
数据污染得到了避免。
init参数如果设置为False,表示不为这个field生成初始化操作,dataclass提供了hook—— __post_init__
供我们利用这一特性:
|
@dataclass class C: a: int b: int c: int = field(init = False ) def __post_init__( self ): self .c = self .a + self .b |
__post_init__在__init__后被调用,我们可以在这里初始化那些需要前置条件的field。
repr参数表示该field是否被包含进repr的输出,compare和hash参数表示field是否参与比较和计算hash值。metadata不被dataclass自身使用,通常让第三方组件从中获取某些元信息时才使用,所以我们不需要使用这一参数。
如果指定一个field的类型注解为dataclasses.InitVar,那么这个field将只会在初始化过程中(__init__和__post_init__)可以被使用,当初始化完成后访问该field会返回一个dataclasses.Field对象而不是field原本的值,也就是该field不再是一个可访问的数据对象。举个例子,比如一个由数据库对象,它只需要在初始化的过程中被访问:
|
@dataclass class C: i: int j: int = None database: InitVar[DatabaseType] = None def __post_init__( self , database): if self .j is None and database is not None : self .j = database.lookup( 'j' ) c = C( 10 , database = my_database) |
这个例子中会返回c.i和c.j的数据,但是不会返回c.database的。
一些常用函数
dataclasses模块中提供了一些常用函数供我们处理数据类。
使用dataclasses.asdict和dataclasses.astuple我们可以把数据类实例中的数据转换成字典或者元组:
|
>>> from dataclasses import asdict, astuple >>> asdict(Lang()) { 'name' : 'python' , 'strong_type' : True , 'static_type' : False , 'age' : 28 } >>> astuple(Lang()) ( 'python' , True , False , 28 ) |
使用dataclasses.is_dataclass可以判断一个类或实例对象是否是数据类:
|
>>> from dataclasses import is_dataclass >>> is_dataclass(Lang) True >>> is_dataclass(Lang()) True |
dataclass继承
python3.7引入dataclass的一大原因就在于相比namedtuple,dataclass可以享受继承带来的便利。
dataclass装饰器会检查当前class的所有基类,如果发现一个dataclass,就会把它的字段按顺序添加进当前的class,随后再处理当前class的field。所有生成的方法也将按照这一过程处理,因此如果子类中的field与基类同名,那么子类将会无条件覆盖基类。子类将会根据所有的field重新生成一个构造函数,并在其中初始化基类。
看个例子:
|
@dataclass class Python(Lang): tab_size: int = 4 is_script: bool = True >>> Python() Python(name = 'python' , strong_type = True , static_type = False , age = 28 , tab_size = 4 , is_script = True ) @dataclass class Base: x: float = 25.0 y: int = 0 @dataclass class C(Base): z: int = 10 x: int = 15 >>> C() C(x = 15 , y = 0 , z = 10 ) |
Lang的field被Python继承了,而C中的x则覆盖了Base中的定义。
没错,数据类的继承就是这么简单。
总结
合理使用dataclass将会大大减轻开发中的负担,将我们从大量的重复劳动中解放出来,这既是dataclass的魅力,不过魅力的背后也总是有陷阱相伴,最后我想提几点注意事项:
- dataclass通常情况下是unhashable的,因为默认生成的__hash__是None,所以不能用来做字典的key,如果有这种需求,那么应该指定你的数据类为frozen dataclass
- 小心当你定义了和dataclass生成的同名方法时会引发的问题
- 当使用可变类型(如list)时,应该考虑使用field的default_factory
- 数据类的属性都是公开的,如果你有属性只需要初始化时使用而不需要在其他时候被访问,请使用dataclasses.InitVar
只要避开这些陷阱,dataclass一定能成为提高生产力的利器。
参考
https://docs.python.org/3.7/library/dataclasses.html
https://www.python.org/dev/peps/pep-0557
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持开心学习网。
原文链接:https://www.cnblogs.com/apocelipes/p/10284346.html
- python3.7.2 详细安装教程(python3.5安装python3-tk详解)
- python3.7手册中文版(Python3.4解释器用法简单示例)
- python3.7对象检测(在Python中使用Neo4j的方法)
- centos7上安装python(centos6.5安装python3.7.1之后无法使用pip的解决方案)
- python3.7不兼容pywinauto(浅谈python编译pyc工程--导包问题解决)
- python3.7标准库官方手册(Python3.7 dataclass使用指南小结)
- python3.7保存文件(详解用python实现基本的学生管理系统文件存储版python3)
- ubuntu python3.7安装(详解Ubuntu16.04安装Python3.7及其pip3并切换为默认版本)
- anaconda如何配置python3.7(WIn10+Anaconda环境下安装PyTorch避坑指南)
- python3.7.0使用方法(Python3.5模块的定义、导入、优化操作图文详解)
- 王治郅菜鸟赛季已让八一带入正轨,大郅七大经典语录或是成功秘诀(王治郅菜鸟赛季已让八一带入正轨)
- 庆八一,重读经典红色语录,感悟互联网发展硬道理(重读经典红色语录)
- TVB新剧《黯夜守护者》将播,陈展鹏陈炜首次合作探讨人性(TVB新剧黯夜守护者将播)
- 新晋小花被称女版吴卓羲 将取代滕丽名成为TVB新一代御用女警(新晋小花被称女版吴卓羲)
- 艺人吴卓羲10年警察生涯,演足10年阿Sir,系咩玩法(艺人吴卓羲10年警察生涯)
- 菲律宾潜水(菲律宾潜水价格)
热门推荐
- mysql索引面试总结(Mysql数据库索引面试题程序员基础技能)
- dedecms保存文章慢(dedeCms批量修改文章发布时间的方法)
- 怎么用腾讯云主机搭建网站(用云主机怎么搭建网站?云主机建网站完整流程)
- windowsserver2008部署php项目(win2008 r2 服务器环境配置FTP/ASP/ASP.Net/PHP)
- sqlserver2016使用教程(SQL Server 2016 Alwayson新增功能图文详解)
- php静态类和动态类的区别(PHP Trait代码复用类与多继承实现方法详解)
- mysql账户访问权限(MySQL 权限控制详解)
- 阿里云服务器安全组在哪(阿里云服务器安全组设置规则)
- phpstudy使用教程最新版(phpstudy安装使用图文教程)
- vue 组件布局(Vue实现固定底部组件的示例)
排行榜
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9