子类化Python namedtuple

时间:2017-06-02 03:39:45

标签: python namedtuple

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。

1 个答案:

答案 0 :(得分:14)

NEW UPDATE:

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

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.namedtupleexcept 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__