Python中的数据类dataclass解读

 更新时间:2024年01月25日 16:36:09   作者:be5yond  
这篇文章主要介绍了Python中的数据类dataclass使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

1. 为什么需要数据类

1.1 ☹️内置数据类型的局限

假设我们现在遇到一个场景, 需要一个数据对象来保存一些运动员信息.

可以选择使用基本的数据类型tuple或者dict实现. 如:创建一个球员jordan, 信息包括球员姓名, 号码,  位置, 年龄.

使用tuple

In [1]: jordan = ('Micheal Jordan', 23, 'PG', 29)
In [2]: jordan[0]
Out[2]: 'Micheal Jordan'

劣势:创建和取值基于位置,  需要记住坐标对应的信息. 

使用dict

In [3]: jordan = {'name': 'Micheal Jordan', 'number': 23, 'position': 'PG', 'age': 29}
In [4]: jordan['position']
Out[4]: 'PG'

使用字典之后, 获取信息时会相对直观, 但是相较于字典的括号语法 jordan["position"] 我们更希望可以用类似获取属性一样使用 jordan.postion. 

劣势:无法对数据属性名进行控制,

少值或者错值,如, jordan = {'name': 'Micheal Jordan', 'NUMBER': 23} 一样可以创建成功.

1.2 😐使用命名元组 namedtuple

为了解决这种问题, python 中的 collections 模块提供一个命名元组, 可以使用点表示法和字段名称访问给定命名元组中的值. 

使用namedtuple代码如下

In [5]: from collections import namedtuple
 
In [6]: Player = namedtuple('Player', ['name', 'number', 'position', 'age', 'grade'])
In [7]: jordan = Player('Micheal Jordan', 23, 'PG', 29, 'S+')
 
In [8]: jordan
Out[8]: Player(name='Micheal Jordan', number=23, position='PG', age=29, grade='S+')

使用namedtuple之后

① 可以使用 '.'语法获取数据的属性, 可以限制数据的属性名称,

② 创建对象时数据不匹配会报错.

In [9]: jordan.number
Out[9]: 23
 
In [10]: bryant = Player('Kobe Bryant', 24, 'PG')
---------------------------------------------------------------------------
TypeError: __new__() missing 2 required positional arguments: 'age' and 'grade'

1.2.1 namedtuple的不足

对于一些字段比较少的数据结构, namedtuple是一个非常好的解决方案.

但面对一些复杂的数据的时候, 需要更多的功能时, namedtuple就无法满足了.

In [11]: bryant = Player('Kobe Bryant', 24, 'PG', 22, 'S')
In [12]: bryant.age=23
---------------------------------------------------------------------------
AttributeError: can't set attribute

劣势:

① 数据无法修改

② 无法自定义数据比较,  没有默认值, 没有函数支持.

1.3 自定义类 Class

为了支持数据修改, 默认值, 比较等功能. 更加好一些的方法是, 使用自定义类来实现数据类.

一个最简单的数据类代码如下:

In [13]: class Player:
    ...:     def __init__(self, name, number, position, age, grade):
    ...:         self.name = name
    ...:         self.number = number
    ...:         self.position = position
    ...:         self.age = age
    ...:         self.grade = grade
 
In [14]: bryant = Player(name='Kobe Bryant', number=24, position='PG', age=22, grade='S')
In [15]: jordan = Player('Micheal Jordan', 23, 'PG', 29, 'S+')

可以使用位置参数或者键值参数创建对象

In [16]: bryant.position='SF'
In [17]: bryant.position
Out[17]: 'SF'

可以看到, 数据类可以支持对属性的修改,

In [18]: bryant
Out[18]: <__main__.Player at 0x29446d401c8>

问题①:目前的实现 对于对象的描述不太友好,

 
In [19]: jordan > bryant
---------------------------------------------------------------------------
TypeError: '>' not supported between instances of 'Player' and 'Player'

问题②:数据还不支持比较. 

为了解决上面两个问题,可以通过实现 __repr__ 方法来自定义描述, 实现 __gt__ 方法来支持比较的功能.

更新代码如下:

In [20]: class Player:
    ...:     def __init__(self, name, number, position, age, grade):
    ...:         self.name = name
    ...:         self.number = number
    ...:         self.position = position
    ...:         self.age = age
    ...:         self.grade = grade
    ...:     def __repr__(self):
    ...:         return f'Player: \n {self.name}\t #{self.number}\t @{self.position}\t <{self.grade}>'
    ...:     def __eq__(self, other):
    ...:         return self.age == other.age
    ...:     def __gt__(self, other):
    ...:         return self.age > other.age
    ...:     def swing(self, pos):
    ...:         self.position = pos
 
In [21]: jordan = Player('Micheal Jordan', 23, 'PG', 29, 'S+')
In [22]: bryant = Player('Kobe Bryant', 24, 'PG', 22, 'S')
 
In [23]: jordan
Out[23]:
Player:
 Micheal Jordan  #23     @PG     <S+>
 
In [24]: jordan > bryant
Out[24]: True
 
In [25]: jordan.swing('SF')
In [26]: jordan
Out[26]:
Player:
 Micheal Jordan  #23     @SF     <S+>

可以看到数据对象有了更直观的描述, 支持了对比 (若要支持 >= 的对比, 还需要自定义 __ge__方法). 还可以自定义方法swing来改变球员打的位置.

劣势:

① __init__方法中重复代码 (示例中每个属性都需要写3遍)

② 需要自己实现__repr__方法, 和比较方法__eq__, __gt__等

1.4 😃数据类 dataclass

主角出场了, 数据类是Python3.7 开始引入的一个新功能, 数据类提供了开箱即用的方法来创建自定义数据, 可以直接实例化、打印和比较数据类实例.

In [1]: from dataclasses import dataclass
 
In [2]: @dataclass
   ...: class Player:
   ...:     name: str
   ...:     number: int
   ...:     position: str
   ...:     age: int
   ...:     grade: str
 
In [3]: james = Player('Lebron James', 23, 'SF', 25, 'S')
In [4]: james
Out[4]: Player(name='Lebron James', number=23, position='SF', age=25, grade='S')

2. dataclass 的使用

2.1 类型提示和默认值

dataclass 可以认为是提供了一个简写__init__方法的语法糖. 

类型注释是必填项 (不限制数据类型时, 添加typing.Any为类型注释), 默认值的传递方式和__init__方法的参数格式一致. 

In [1]: from dataclasses import dataclass
In [2]: from typing import Any
 
In [3]: @dataclass
   ...: class Data:
   ...:     name: Any
   ...:     value: Any = 42

2.2 数据嵌套

数据类可以嵌套为其他数据类的字段,  可以简单创建一个有2个队员的球队.lal包含两名球员 james和davis

In [1]: from dataclasses import dataclass
In [2]: from typing import List
 
In [3]: @dataclass
   ...: class Player:
   ...:     name: str
   ...:     number: int
   ...:     position: str
   ...:     age: int
   ...:     grade: str
 
In [4]: @dataclass
   ...: class Team:
   ...:     name: str
   ...:     players: List[Player]
 
In [5]: james = Player('Lebron James', 23, 'SF', 25, 'S')
In [6]: davis = Player('Anthony Davis', 3, 'PF', 21, 'S-')
 
In [7]: lal = Team('Los Angeles Lakers', [james, davis])
In [8]: lal
Out[8]: Team(name='Los Angeles Lakers', players=[Player(name='Lebron James', number=23, position='SF', age=25, grade='S'), Player(name='Anthony Davis', number=3, position='PF', age=21, grade='S-')])

2.3 dataclasses中的field

当我们尝试使用可变的数据类型, 给数据类中做默认值时, 触发了python中的大坑之一 使用可变默认参数, 导致多个实例公用一个数据从而引发bug. 

dataclass 默认阻止使用可变数据做默认值

In [9]: @dataclass
   ...: class Team:
   ...:     name: str
   ...:     players: List[Player] = [james]
---------------------------------------------------------------------------
ValueError: mutable default <class 'list'> for field players is not allowed: use default_factory

就像错误提示中的, 处理此种场景时, 需要使用 field 中的 default_factory .

In [10]: from dataclasses import field
 
In [11]: @dataclass
    ...: class Team:
    ...:     name: str
    ...:     players: List[Player] = field(default_factory=lambda :[james])
 
In [12]: nyk = Team('New York Knicks')
In [13]: nyk
Out[13]: Team(name='New York Knicks', players=[Player(name='Lebron James', number=23, position='SF', age=25, grade='S')])
field 支持的参数
参数描述默认值
default字段的默认值
default_factory返回字段初始值的函数
init是否在.__init__()方法中使用字段True
repr是否在.__repr__()方法中使用字段True
compare是否在比较对象时, 包括该字段True
hash计算hash时, 是否包括字段True
metadata包含字段信息的映射

2.4 不可变数据类

要使数据类不可变,需要在创建类时设置frozen=True。

In [1]: from dataclasses import dataclass
In [2]: from typing import Any
 
In [3]: @dataclass(frozen=True)
   ...: class Data:
   ...:     name: Any
   ...:     value: Any = 42
 
In [4]: data = Data('myname', 99)
In [4]: data.name = 'other'
---------------------------------------------------------------------------
FrozenInstanceError: cannot assign to field 'name'
 

总结

dataclass 提供一个简便的方式创建数据类, 默认实现__init__(),  __repr__(),  __eq__()方法.

dataclass支持数据类型的嵌套

支持将数据设置为不可变

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Linux下Python获取IP地址的代码

    Linux下Python获取IP地址的代码

    这篇文章主要介绍了Linux下Python获取IP地址的代码,需要的朋友可以参考下
    2014-11-11
  • python获取一组汉字拼音首字母的方法

    python获取一组汉字拼音首字母的方法

    这篇文章主要介绍了python获取一组汉字拼音首字母的方法,涉及Python针对汉字操作的相关技巧,需要的朋友可以参考下
    2015-07-07
  • Python中的time和datetime模块使用方法详解

    Python中的time和datetime模块使用方法详解

    Python 中的 time 和 datetime 模块是处理时间和日期的重要工具,它们可以执行各种操作,如获取当前时间、格式化日期、计算时间差等,本文将分享这两个模块的使用方法,包括安装、基本功能、日期时间对象、时间戳、时间间隔、日期时间格式化和示例代码
    2023-11-11
  • python使用Matplotlib改变坐标轴的默认位置

    python使用Matplotlib改变坐标轴的默认位置

    这篇文章主要为大家详细介绍了python使用Matplotlib改变坐标轴的默认位置,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-10-10
  • Pycharm安装scrapy及初始化爬虫项目的完整步骤

    Pycharm安装scrapy及初始化爬虫项目的完整步骤

    因为入门python以来一直使用pycharm,所以对着黑白的DOS不习惯,所以此次来实现使用pycharm进行实现使用scrapy框架,下面这篇文章主要给大家介绍了关于Pycharm安装scrapy及初始化爬虫项目的完整步骤,需要的朋友可以参考下
    2022-08-08
  • Python实现PS图像调整黑白效果示例

    Python实现PS图像调整黑白效果示例

    这篇文章主要介绍了Python实现PS图像调整黑白效果,结合实例形式分析了Python实现PS图像的黑白效果原理与相关操作技巧,需要的朋友可以参考下
    2018-01-01
  • python实现的登录与提交表单数据功能示例

    python实现的登录与提交表单数据功能示例

    这篇文章主要介绍了python实现的登录与提交表单数据功能,结合实例形式分析了Python表单登录相关的请求与响应操作实现技巧,需要的朋友可以参考下
    2019-09-09
  • Python super( )函数用法总结

    Python super( )函数用法总结

    今天给大家带来的知识是关于Python的相关知识,文章围绕着super( )函数展开,文中有非常详细的介绍及代码示例,需要的朋友可以参考下
    2021-06-06
  • 在Python中实现贪婪排名算法的教程

    在Python中实现贪婪排名算法的教程

    这篇文章主要介绍了在Python中实现贪婪排名算法的教程,也是对学习算法的一个很好的演示,需要的朋友可以参考下
    2015-04-04
  • 深入探究python中Pandas库处理缺失数据和数据聚合

    深入探究python中Pandas库处理缺失数据和数据聚合

    在本篇文章中,我们将深入探讨Pandas库中两个重要的数据处理功能:处理缺失数据和数据聚合,文中有详细的代码示例,对我们的学习或工作有一定的帮助,需要的朋友可以参考下
    2023-07-07

最新评论