什么是数据类?它与普通类有何不同?

2025-03-05 09:16:00
admin
原创
83
摘要:问题描述:PEP 557将数据类引入 Python 标准库。它表示,通过应用@dataclass下面显示的装饰器,它将生成“除其他外,一个__init__()”。from dataclasses import dataclass @dataclass class InventoryItem: &qu...

问题描述:

PEP 557将数据类引入 Python 标准库。它表示,通过应用@dataclass下面显示的装饰器,它将生成“除其他外,一个__init__()”。

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

它还说数据类是“具有默认值的可变命名元组”,但我不明白这是什么意思,也不明白数据类与普通类有何不同。

什么是数据类以及何时最适合使用它们?


解决方案 1:

数据类只是用于存储状态的常规类,而不是包含大量逻辑。每次创建一个主要由属性组成的类时,您都会创建一个数据类。

dataclasses模块的作用是使创建数据类变得更容易。它为您处理了很多样板工作。

当你的数据类必须是可哈希的时候,这尤其有用;因为这需要一个__hash__方法和一个__eq__方法。如果你__repr__为了方便调试而添加自定义方法,那么这可能会变得非常冗长:

class InventoryItem:
    '''Class for keeping track of an item in inventory.'''
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def __init__(
            self, 
            name: str, 
            unit_price: float,
            quantity_on_hand: int = 0
        ) -> None:
        self.name = name
        self.unit_price = unit_price
        self.quantity_on_hand = quantity_on_hand

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand
    
    def __repr__(self) -> str:
        return (
            'InventoryItem('
            f'name={self.name!r}, unit_price={self.unit_price!r}, '
            f'quantity_on_hand={self.quantity_on_hand!r})'
        )

    def __hash__(self) -> int:
        return hash((self.name, self.unit_price, self.quantity_on_hand))

    def __eq__(self, other) -> bool:
        if not isinstance(other, InventoryItem):
            return NotImplemented
        return (
            (self.name, self.unit_price, self.quantity_on_hand) == 
            (other.name, other.unit_price, other.quantity_on_hand))

dataclasses可以将其简化为:

from dataclasses import dataclass

@dataclass(unsafe_hash=True)
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

(基于PEP 示例的示例)。

相同的类装饰器还可以生成比较方法(__lt____gt__等)并处理不变性。

namedtuple类也是数据类,但默认情况下是不可变的(也是序列)。dataclasses在这方面更加灵活,并且可以轻松地构造,以便它们可以充当与类相同的角色namedtuple

该 PEP 受到attrs项目的启发,它可以做更多的事情(包括插槽、验证器、转换器、元数据等)。

如果您想看一些示例,我最近使用了dataclasses几个Advent of Code解决方案,请参阅第 7 天、第 8 天、第 11 天和第 20 天的解决方案。

dataclasses如果您想在 Python 版本 <3.7 中使用模块,那么您可以安装反向移植的模块(需要 3.6)或使用attrs上面提到的项目。

解决方案 2:

概述

问题已经得到解决。但是,这个答案添加了一些实际示例,以帮助对数据类进行基本的理解。

Python 数据类到底是什么,什么时候最适合使用它们?

  1. 代码生成器:生成样板代码;您可以选择在常规类中实现特殊方法,或者让数据类自动实现它们。

  2. 数据容器:保存数据的结构(例如元组和字典),通常以点分隔,属性访问(例如类namedtuple)等。

“具有默认值的可变命名元组”

后一句话的意思如下:

  • mutable:默认情况下,数据类属性可以重新分配。您可以选择将它们设为不可变(请参阅下面的示例)。

  • namedtuple:您有点状,像namedtuple常规类一样访问属性。

  • default:您可以为属性分配默认值。

与普通类相比,您主要可以节省输入样板代码的时间。


特征

这是数据类特征的概述(TL;DR?请参阅下一节中的摘要表)。

您可获得什么

以下是您从数据类默认获得的功能。

属性+表示+比较

import dataclasses


@dataclasses.dataclass
#@dataclasses.dataclass()                                       # alternative
class Color:
    r : int = 0
    g : int = 0
    b : int = 0

这些默认值是通过自动设置以下关键字来提供的True

@dataclasses.dataclass(init=True, repr=True, eq=True)

您可以打开哪些功能

如果设置了适当的关键字,则可以提供附加功能True

命令

@dataclasses.dataclass(order=True)
class Color:
    r : int = 0
    g : int = 0
    b : int = 0

现已实现排序方法(重载运算符:)< > <= >=,类似于functools.total_ordering更强的相等性测试。

可哈希,可变

@dataclasses.dataclass(unsafe_hash=True)                        # override base `__hash__`
class Color:
    ...

尽管对象可能是可变的(可能是不受欢迎的),但还是实现了哈希。

可哈希,不可变

@dataclasses.dataclass(frozen=True)                             # `eq=True` (default) to be immutable 
class Color:
    ...

现已实现哈希,并且不允许更改对象或分配给属性。

unsafe_hash=True总体而言,如果 或,则该对象是可哈希的frozen=True

另请参阅原始哈希逻辑表以了解更多详细信息。

优化

@dataclasses.dataclass(slots=True)              # py310+
class SlottedColor:
    #__slots__ = ["r", "b", "g"]                # alternative
    r : int
    g : int
    b : int

物体尺寸现已减小:

>>> imp sys
>>> sys.getsizeof(Color)
1056
>>> sys.getsizeof(SlottedColor)
888

slots=True已添加到 Python 3.10 中。(感谢@ajskateboarder)。

在某些情况下,slots=True/__slots__还可以提高创建实例和访问属性的速度。此外,插槽不允许默认分配;否则,ValueError会引发 a。如果__slot__已经存在,slots=True将导致 a TypeError

请参阅此博客文章中有关插槽的更多信息。

查看有关Python 3.10+ 中添加的更多参数:match_args,,,。kw_only`slots`weakref_slot

你得不到什么

为了获得以下功能,必须手动实现特殊方法:

拆开

@dataclasses.dataclass
class Color:
    r : int = 0
    g : int = 0
    b : int = 0

    def __iter__(self):
        yield from dataclasses.astuple(self)

摘要表

+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+
|       Feature        |       Keyword        |                      Example                       |           Implement in a Class          |
+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+
| Attributes           |  init                |  Color().r -> 0                                    |  __init__                               |
| Representation       |  repr                |  Color() -> Color(r=0, g=0, b=0)                   |  __repr__                               |
| Comparision*         |  eq                  |  Color() == Color(0, 0, 0) -> True                 |  __eq__                                 |
|                      |                      |                                                    |                                         |
| Order                |  order               |  sorted([Color(0, 50, 0), Color()]) -> ...         |  __lt__, __le__, __gt__, __ge__         |
| Hashable             |  unsafe_hash/frozen  |  {Color(), {Color()}} -> {Color(r=0, g=0, b=0)}    |  __hash__                               |
| Immutable            |  frozen + eq         |  Color().r = 10 -> TypeError                       |  __setattr__, __delattr__               |
| Optimization         |  slots               |  sys.getsizeof(SlottedColor) -> 888                |  __slots__                              |
|                      |                      |                                                    |                                         |
| Unpacking+           |  -                   |  r, g, b = Color()                                 |  __iter__                               |
+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+
  • __ne__不需要,因此未实现。

+这些方法不会自动生成,需要在数据类中手动实现。


其他功能

初始化后

@dataclasses.dataclass
class RGBA:
    r : int = 0
    g : int = 0
    b : int = 0
    a : float = 1.0

    def __post_init__(self):
        self.a : int =  int(self.a * 255)


RGBA(127, 0, 255, 0.5)
# RGBA(r=127, g=0, b=255, a=127)

遗产

@dataclasses.dataclass
class RGBA(Color):
    a : int = 0

转换

递归地将数据类转换为元组或字典:

>>> dataclasses.astuple(Color(128, 0, 255))
(128, 0, 255)
>>> dataclasses.asdict(Color(128, 0, 255))
{'r': 128, 'g': 0, 'b': 255}

限制

  • 缺乏处理带星号参数的机制

  • 使用嵌套数据类可能很复杂


参考

  • R. Hettinger关于Dataclasses 的演讲:终结所有代码生成器的代码生成器

  • T. Hunner 的演讲:更简单的类:没有繁琐内容的 Python 类

  • Python关于哈希细节的文档

  • Real Python 的Python 3.7 数据类终极指南

  • A. Shaw 的博客文章《Python 3.7 数据类简介

  • E. Smith 的github上关于dataclasses 的仓库

解决方案 3:

来自PEP 规范:

提供了一个类装饰器,用于检查类定义中是否有带类型注释的变量,如 PEP 526“变量注释语法”中定义的那样。在本文档中,此类变量称为字段。装饰器使用这些字段将生成的方法定义添加到类中,以支持实例初始化、repr、比较方法以及“规范”部分中描述的可选其他方法。这样的类称为数据类,但该类实际上并没有什么特别之处:装饰器将生成的方法添加到类中并返回给定的相同类。

生成@dataclass器向类中添加方法,否则您必须自己定义这些方法__repr__,例如__init__、、__lt____gt__

解决方案 4:

考虑这个简单的类Foo

from dataclasses import dataclass
@dataclass
class Foo:    
    def bar():
        pass  

这是dir()内置的比较。左侧是Foo没有 @dataclass 装饰器的,右侧是有 @dataclass 装饰器的。

在此处输入图片描述

inspect这是使用模块进行比较后的另一个差异。

在此处输入图片描述

解决方案 5:

@dataclass 可以轻松创建用于存储数据的类,而无需编写额外的代码。它会自动创建init () 和其他方法,从而使您的代码更简洁、更易读。例如:如果您创建一个类来存储数据,则必须编写:

class Person:
    def __init__(self, name, age):
        self.n

name = name
        self.age = age

    def __repr__(self):  # So we can print the object nicely
        return f"Person(name='{self.name}', age={self.age})"

p1 = Person("Alice", 25)
print(p1)  # Output: Person(name='Alice', age=25)

对于简单的任务来说代码太多了,但是使用@dataclass我们可以使它变得更简单:

from dataclasses import dataclass

@dataclass
class Person:
    name: str
    age: int

p1 = Person("Alice", 25)
print(p1)  # Output: Person(name='Alice', age=25)
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   3983  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   2747  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Freshdesk、ClickUp、nTask、Hubstaff、Plutio、Productive、Targa、Bonsai、Wrike。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在项目管理过程中面临着诸多痛点,如任务分配不...
项目管理系统   82  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Monday、TeamGantt、Filestage、Chanty、Visor、Smartsheet、Productive、Quire、Planview。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多项目经理和团队在管理复杂项目时,常...
开源项目管理工具   90  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Smartsheet、GanttPRO、Backlog、Visor、ResourceGuru、Productive、Xebrio、Hive、Quire。在当今快节奏的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在选择项目管理工具时常常面临困惑:...
项目管理系统   79  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用