为什么namedtuple._make检查返回值的长度?

时间:2017-05-02 20:24:24

标签: python-3.x namedtuple

在Python中覆盖namedtuple个对象的length方法比你想象的要冗长乏味。天真的方法,

from collections import namedtuple

class Rule(namedtuple('Rule', ['lhs', 'rhs'])):
    def __len__(self):
        return len(self.rhs)

r = Rule('S', ['NP', 'Infl', 'VP'])
new_r = r._replace(lhs='CP') # raises a TypeError

不起作用。如果您检查类的实际源代码(可用作_source属性),则可以看到_make_replace调用哪些调用以及哪些引发错误)的实现方式如下这样:

@classmethod
def _make(cls, iterable, new=tuple.__new__, len=len):
    'Make a new Rule object from a sequence or iterable'
    result = new(cls, iterable)
    if len(result) != 2:
        raise TypeError('Expected 2 arguments, got %d' % len(result))
    return result

有趣的是,它检查以确保返回值的长度为2.这使得覆盖元组上的__len__方法变得更加困难,因为_make如果返回值则会抱怨长度不是2。

通过传递始终返回2到len的“_make”函数可以防止此行为:

from collections import namedtuple

class Rule(namedtuple('Rule', ['lhs', 'rhs'])):
    def _make(self, *args, len=lambda _: 2, **kwargs):
        return super()._make(*args, len=len, **kwargs)

    def __len__(self):
        return len(self.rhs)

r = Rule('S', ['NP', 'Infl', 'VP'])
new_r = r._replace(lhs='CP') # fine

我的问题是,为什么这个长度检查必须首先,并且覆盖_make是否安全,所以它不会这样做?

1 个答案:

答案 0 :(得分:6)

_make检查返回值的长度,因为namedtuples是固定长度的,_make必须强制执行。如果没有,你可以做

Point = namedtuple('Point', ['x', 'y'])
p1 = Point._make([1, 2, 3])
p2 = Point._make([1])

并获得一个没有y的Point和一个带有额外条目的Point悬挂在最后。

_make无法检查参数的长度,因为参数可能是一个不支持len的任意迭代,因此返回值为&#39}。长度是最方便检查的。

请勿覆盖_make以绕过此检查。你的对象远远超出了一个命名元组的概念 - 哎呀,远远超过元组的概念 - 你根本不应该使用namedtuple或任何元组子类。只需写一个普通的课程。