Python 将字段添加到NamedTuple,使其与以前的字段保持一致

Python 将字段添加到NamedTuple,使其与以前的字段保持一致,python,python-3.x,namedtuple,Python,Python 3.x,Namedtuple,假设我想存储一些关于会议日程安排的信息,包括演示时间和暂停时间。我可以在名为tuple的中执行此操作 from typing import NamedTuple class BlockTime(NamedTuple): t_present: float t_pause: float 但是,如果我还想存储每个块将花费多少,以便t_each=t_pause+t_present,我不能将其作为属性添加: class BlockTime(NamedTuple): t_pres

假设我想存储一些关于会议日程安排的信息,包括演示时间和暂停时间。我可以在名为tuple的
中执行此操作

from typing import NamedTuple

class BlockTime(NamedTuple):
    t_present: float
    t_pause: float
但是,如果我还想存储每个块将花费多少,以便
t_each=t_pause+t_present
,我不能将其作为属性添加:

class BlockTime(NamedTuple):
    t_present: float
    t_pause: float
    # this causes an error
    t_each = t_present + t_pause

在Python中执行此操作的正确方法是什么?如果我创建了一个
\uuuu init\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoself)
方法并将其作为实例变量存储在那里,那么它将是可变的。

您可以创建一个构建
BlockTime
对象

class BlockTime(NamedTuple):
    t_present: float
    t_pause: float
    t_each: float
    @classmethod
    def factory(cls, present, pause):
        return cls(present, pause, present+pause)

print(BlockTime.factory(1.0, 2.0))
# BlockTime(t_present=1.0, t_pause=2.0, t_each=3.0)
编辑:

下面是一个使用新的Python3.7的解决方案


但它们非常接近,这让您可以创建外观自然的实例
BlockTime(1.0,2.0)

,如果它不是真正存储的,而是动态计算的,您可以使用一个简单的
属性

from typing import NamedTuple

class BlockTime(NamedTuple):
    t_present: float
    t_pause: float
    @property
    def t_each(self):
        return self.t_present + self.t_pause

>>> b = BlockTime(10, 20)
>>> b.t_each  # only available as property, not in the representation nor by indexing or iterating
30
这样做的好处是,您永远不会(甚至不会意外地)为其存储错误的值。然而,这是以根本没有实际存储它为代价的。因此,为了让它看起来像是被存储的,你必须至少覆盖
\uuuu getitem\uuuuuuu
\uuuu iter\uuuuuuuuu
\uuuuu repr\uuuuuu
,这可能太麻烦了

例如,Patrick Haugh给出的
NamedTuple
方法有一个缺点,即仍然可能创建不一致的
BlockTime
s或丢失部分
NamedTuple
便利性:

>>> b = BlockTime.factory(1.0, 2.0)
>>> b._replace(t_present=20)
BlockTime(t_present=20, t_pause=2.0, t_each=3.0)

>>> b._make([1, 2])
TypeError: Expected 3 arguments, got 2

实际上,您有一个“计算”字段,它必须与其他字段同步,这一事实已经表明,您可能根本不应该存储它,以避免状态不一致。

。。不能重写父级为tuple的类的
\uuuu new\uuu
\uuu init\uuuu
。但是您可以覆盖一个类的
\uuuu new\uuu
,该类继承自另一个父类名为tuple的类

所以你可以这样做

from typing import NamedTuple

class BlockTimeParent(NamedTuple):
    t_present: float
    t_pause: float
    t_each: float

class BlockTime(BlockTimeParent):
    def __new__(cls, t_present, t_pause):
        return super().__new__(cls, t_present, t_pause, t_present+ t_pause)

b = BlockTime(1,2)
print (b)
# BlockTime(t_present=1, t_pause=2, t_each=3)

请注意,dataclass实例不是序列,而namedtuple是(
BlockTime(1.0,2.0)[-1]
工作,正如
list(BlockTime(1.0,2.0))
工作一样)。这是可以解决的;有关示例,请参见。
from typing import NamedTuple

class BlockTimeParent(NamedTuple):
    t_present: float
    t_pause: float
    t_each: float

class BlockTime(BlockTimeParent):
    def __new__(cls, t_present, t_pause):
        return super().__new__(cls, t_present, t_pause, t_present+ t_pause)

b = BlockTime(1,2)
print (b)
# BlockTime(t_present=1, t_pause=2, t_each=3)