子类化集合命名元组

Posted

技术标签:

【中文标题】子类化集合命名元组【英文标题】:Subclassing collections namedtuple 【发布时间】:2017-11-03 08:39:16 【问题描述】:

Python 的 namedtuple 作为一个轻量级的、不可变的数据类非常有用。我喜欢将它们用于簿记参数而不是字典。当需要更多功能时,例如简单的文档字符串或默认值,您可以轻松地将 namedtuple 重构为一个类。但是,我见过从 namedtuple 继承的类。他们获得了什么功能,失去了什么性能?例如,我会将其实现为

from collections import namedtuple

class Pokemon(namedtuple('Pokemon', 'name type level')):
    """
    Attributes
    ----------
    name : str
        What do you call your Pokemon?
    type : str
        grass, rock, electric, etc.
    level : int
        Experience level [0, 100]
    """
     __slots__ = ()

仅出于能够干净地记录 attrs 的目的,__slots__ 用于防止创建 __dict__(保持命名元组的轻量级特性)。

对于记录参数是否有更好的轻量级数据类推荐?注意我使用的是 Python 2.7。

【问题讨论】:

另请参阅这篇关于替代数据容器***.com/a/47784683/4531270 的帖子,即NamedTuple 和数据类。 【参考方案1】:

新更新:

在 python 3.6+ 中,您可以使用新的类型化语法并创建一个typing.NamedTuple。新语法支持所有常见的 python 类创建功能(文档字符串、多重继承、默认参数、方法等从 3.6.1 开始可用):

import typing

class Pokemon(MyMixin, typing.NamedTuple):
    """
    Attributes
    ----------
    name : str
        What do you call your Pokemon?
    type : str
        grass, rock, electric, etc.
    level : int
        Experience level [0, 100]
    """
    name: str
    type: str
    level: int = 0 # 3.6.1 required for default args

    def method(self):
        # method work

这个版本创建的类对象大多等同于原来的collections.namedtuple、except for a few details。

您也可以使用与旧命名元组相同的语法:

Pokemon = typing.NamedTuple('Pokemon', [('name', str), ('type', str), ('level', int)])

原答案


简答:no, unless you are using Python < 3.5

P3 docs 似乎非常清楚地暗示,除非您需要添加计算字段(即描述符),否则子类化 namedtuple 不被视为规范方法。这是因为您可以直接更新文档字符串(从 3.5 开始,它们现在是可写的!)。

子类化对于添加新的存储字段没有用处。相反,只需从 _fields 属性创建一个新的命名元组类型...

可以通过直接分配给__doc__ 字段来自定义文档字符串...

更新:

在最新版本的 Python 中,轻量级数据类现在还有其他几个引人注目的可能性。

一个是types.SimpleNamespace (Python 3.3 and later)。它的结构不像namedtuple,但结构并不总是必要的。

关于SimpleNamespace 需要注意的一点:默认情况下,需要在实例化类时显式指定字段名称。不过,这可以很容易地解决,只需致电super().__init__

from types import SimpleNamespace

class Pokemon(SimpleNamespace):
    """
    Attributes
    ----------
    name : str
        What do you call your Pokemon?
    type : str
        grass, rock, electric, etc.
    level : int
        Experience level [0, 100]
    """
    __slots__ = ("name", "type", "level")
    # note that use of __init__ is optional
    def __init__(self, name, type, level):
        super().__init__(name=name, type=type, level=level)

另一个有趣的选项——which is available as of Python 3.7——是dataclasses.dataclass(另见PEP 557):

from dataclasses import dataclass

@dataclass
class Pokemon:
    __slots__ = ("name", "type", "level")
    name: str  # What do you call your Pokemon?
    type: str  # grass, rock, electric, etc.
    level: int = 0  # Experience level [0, 100]

请注意,默认情况下,这两个建议都是可变的,__slots__ 不是任何一个都需要。

【讨论】:

当在 NamedTuple 旁边继承任何类时,其他类都将被忽略。因此,在第一个示例中,Pokemon 不能使用在 MyMixin 中声明的方法,您可以在此处查看我的问题以获取更多信息:***.com/questions/60707607/…

以上是关于子类化集合命名元组的主要内容,如果未能解决你的问题,请参考以下文章

Scala之元组Set和map集合

Python3组合数据类型(元组列表集合字典)语法

了解 JSONEncoder 的子类化

NSLog'd 时子类化的 NSManagedObject 不调用描述

第三章使用集合相关数据

为啥 Python 不支持记录类型? (即可变的命名元组)