代码之家  ›  专栏  ›  技术社区  ›  kingJulian

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

  •  366
  • kingJulian  · 技术社区  · 8 年前

    具有 PEP 557 将数据类引入python标准库。

    他们利用 @dataclass decorator和它们应该是“带默认值的可变命名偶”,但我不太清楚这到底意味着什么,以及它们与常见类的区别。

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

    4 回复  |  直到 8 年前
        1
  •  399
  •   aerin    5 年前

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

    什么 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))
    

    具有 数据类 您可以将其减少到:

    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 类也是数据类,但在默认情况下是不可变的(也是序列)。 数据类 在这方面要灵活得多,并且可以很容易地进行结构化 fill the same role as a namedtuple class .

    政治公众人物的灵感来自 attrs project ,它可以做更多的事情(包括插槽、验证器、转换器、元数据等)。

    如果你想看一些例子,我最近使用 数据类 对于我的几个 Advent of Code 解决方案,请参阅解决方案 day 7 , day 8 , day 11 day 20 .

    如果要使用 数据类 Python版本中的模块<3.7,则可以安装 backported module (要求3.6)或使用 属性 上述项目。

        2
  •  207
  •   pdaawr    5 年前

    概述

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

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

    1. 代码生成器 :生成样板代码;您可以选择在常规类中实现特殊方法,或者让dataclass自动实现它们。
    2. 数据容器 :保存数据的结构(例如元组和dict),通常具有点式属性访问,例如 classes, namedtuple and others .

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

    以下是后一个短语的含义:

    • 可变的 :默认情况下,可以重新分配数据类属性。您可以选择使其不可变(请参见下面的示例)。
    • 命名双倍 :您有点式的属性访问权限,如 命名双倍 或者是普通班。
    • 违约 :可以为属性指定默认值。

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


    特征

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

    你得到了什么

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

    属性+表示+比较

    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)
    

    您可以打开的内容

    如果将相应的关键字设置为 真的 .

    顺序

    @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 .

    另请参见原件 hashing logic table 还有更多细节。

    你没有得到什么

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

    打开包装

    @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 已提出。

    有关插槽的更多信息,请参见 blog post .


    汇总表

    +----------------------+----------------------+----------------------------------------------------+-----------------------------------------+
    |       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__ 不需要,因此 not implemented .


    其他功能

    初始化后

    @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
    

    转换

    将数据类转换为元组或dict, recursively :

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

    局限性


    工具书类

    • R、 赫廷格氏 talk 在…上 Dataclasses:结束所有代码生成器的代码生成器
    • T、 亨纳氏 talk 在…上 更简单的类:没有所有缺点的Python类
    • Python的 documentation 关于哈希详细信息
    • 真正的Python guide 在…上 Python 3.7中数据类的最终指南
    • A、 肖氏 blog post 在…上 Python 3.7数据类简介
    • E、 史密斯的 github repository 在…上 数据类
        3
  •  5
  •   Mahmoud Hanafy    8 年前

    PEP specification :

    提供了一个类装饰器,用于检查 具有PEP 526“语法”中定义的类型注释的变量 变量注释”。在本文档中,此类变量称为 领域。使用这些字段,decorator添加生成的方法 类的定义以支持实例初始化、repr、, 比较方法,以及(可选)中所述的其他方法 规范部分。这样的类称为数据类,但是 “这门课真的没什么特别的,”装饰师补充道 将生成的方法返回给该类,并返回与该类相同的类 鉴于

    这个 @dataclass generator将方法添加到您自己定义的类中 __repr__ , __init__ , __lt__ __gt__ .

        4
  •  3
  •   prosti    7 年前

    考虑一下这个简单的类 Foo

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

    这是 dir() 内置比较。左侧是 Foo公司 没有@dataclass decorator,右边是@dataclass decorator。

    enter image description here

    在使用 inspect 用于比较的模块。

    enter image description here