当前位置: 首页 > 知识库问答 >
问题:

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

庞阳波
2023-03-14

使用PEP 557,将数据类引入python标准库。

它们使用了@dataclass装饰器,它们应该是“带默认值的可变名称耦合”,但我不太清楚这到底意味着什么,以及它们与普通类的区别。

python数据类到底是什么?什么时候最好使用它们?

共有3个答案

狄玉书
2023-03-14

根据PEP规范:

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

@dataclass生成器将方法添加到类中,否则您将定义自己,如__repr____init____lt____gt__

黄向明
2023-03-14

这个问题已经解决了。然而,这个答案增加了一些实际的例子来帮助对数据类的基本理解。

python数据类到底是什么?什么时候最好使用它们?

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

“可变名称与默认值的耦合”

下面是后一个短语的意思:

  • 可变:默认情况下,可以重新分配数据类属性。您可以选择使它们不可变(参见下面的示例)
  • namedtuple:您有点式的属性访问,比如namedtuple或常规类
  • 默认值:可以为属性指定默认值

与普通类相比,您主要节省了键入样板代码。

这是数据类功能的概述(TL;DR?请参阅下一节中的汇总表)。

以下是默认情况下从DataClass获得的功能。

属性表示比较

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

现在实现了排序方法(重载运算符:

易变的,易变的

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

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

可利用的,不可改变的

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

现在实现了散列,不允许更改对象或指定属性。

总的来说,如果unsafe_hash=Truefreezed=True,则对象是可散列的。

详细信息请参见原始散列逻辑表。

要获得以下功能,必须手动执行特殊方法:

拆包

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

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

优化

@dataclasses.dataclass
class SlottedColor:
    __slots__ = ["r", "b", "g"]
    r : int
    g : int
    b : int

对象大小现在减小:

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

在某些情况下,__slots__还提高了创建实例和访问属性的速度。此外,插槽不允许默认分配;否则,将引发ValueError

在这篇博文中可以看到更多关于老虎机的信息。

+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+
|       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__               |
|                      |                      |                                                    |                                         |
| Unpacking+           |  -                   |  r, g, b = Color()                                 |   __iter__                              |
| Optimization+        |  -                   |  sys.getsizeof(SlottedColor) -> 888                |  __slots__                              |
+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+

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

*__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关于数据类的演讲:结束所有代码生成器的代码生成器
  • T.Hunner关于更简单的类的演讲:没有所有粗糙的Python类
  • Python关于散列细节的文档
  • 关于Python 3.7中数据类的终极指南的Real Python指南
  • A.Shaw关于Python 3.7数据类简介的博文
  • E.Smith的github数据类存储库

桂玉石
2023-03-14

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

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

当数据类必须是可散列的时,这尤其有用;因为这需要一个u__方法以及一个__方法。如果添加自定义的_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))

使用数据类可以将其简化为:

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

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

namedtuple类也是数据类,但在默认情况下是不可变的(以及序列)<代码>数据类在这方面要灵活得多,并且可以很容易地进行结构化,以便它们可以担任与命名类相同的角色。

PEP的灵感来源于attrs项目,该项目可以做更多的事情(包括插槽、验证器、转换器、元数据等)。

如果您想查看一些示例,我最近使用了dataclass作为我的几个代码降临解决方案,请参阅第7天、第8天、第11天和第20天的解决方案

如果您想在Python版本中使用数据类模块

 类似资料:
  • 问题内容: 什么是“非阻塞”并发,它与使用线程的普通并发有何不同?为什么在所有需要并发的场景中不使用非阻塞并发呢?使用非阻塞并发是否有开销? 我听说Java中提供了非阻塞并发。在某些特殊情况下,我们应该使用此功能吗? 在集合中使用这些方法之一有什么区别或优势?权衡是什么? 第三季度示例: 与 这些问题更多是从学习/理解的角度来看的。感谢您的关注。 问题答案: 什么是非阻塞并发?它有什么不同。 正式

  • 问题内容: 直到几天前,我才听说过野车,在读完我的老师的Java书籍后,我仍然不确定它的用途以及为什么需要使用它。 比方说,我有一个超类和几个子类都,,,等…现在我需要有动物名单,我首先想到的会是这样的: 相反,我的同事们建议采取以下措施: 为什么要使用通配符而不是简单的泛型? 假设我需要一个get / set方法,应该使用前一种还是后一种?它们有何不同? 问题答案: 声明局部变量时,通配符没有多

  • 本文向大家介绍区块链分类账与普通分类账有何不同?相关面试题,主要包含被问及区块链分类账与普通分类账有何不同?时的应答技巧和注意事项,需要的朋友参考一下 回答:主要区别在于,区块链是一种数字账本,可以很容易地分散,与原始账本相比,区块链账本中的错误机会要少得多。区块链自动执行其所有任务,而在普通分类账中,每项任务都是手动或人工完成的。

  • 问题内容: 如标题所述,有哪些不同的文档类型?它们的含义是什么?我注意到从切换到IE7时,布局看起来有些不同 至 还有其他的吗?影响或后果是什么? 谢谢! 问题答案: 一个 文档类型 或 文档类型声明 关联与文档 的文档类型定义 。 该 文档类型定义 是一个XML文档的标准。XML和XHTML文档都有许多DTD。XML本身并没有太多的架构或一组非常具体的规则,除了要求一切都必须格式正确。您可以将D

  • 问题内容: 我们知道根目录是/,根据posix,还有另一个目录//与/不同。当使用ls /和ls //时,输出与stat相同,但是,如果cd /和cd //则输出不同,尽管目录内容相同。那真的让我感到困惑。有人得到答案了吗? 问题答案: 从Bash常见问题解答:

  • 问题内容: 在Java中,嵌套类是什么,它们做什么? 问题答案: 它们只是其他类中的类。它们使类的层次结构成为可能,如果您将它们设为私有,则这是一种使用它们封装不在类外部公开的数据的便捷方法。Sun有一个简短的教程关于他们