【问题标题】:Why does namedtuple._make check the return value's length?为什么 namedtuple._make 检查返回值的长度?
【发布时间】:2017-05-02 20:24:23
【问题描述】:

在 Python 中覆盖 namedtuple 对象的长度方法比您想象的要繁琐一些。天真的方法,

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 是否安全,所以它不会这样做?

【问题讨论】:

  • “在 Python 中覆盖 namedtuple 对象的 length 方法比你想象的要繁琐一些”——你为什么要这样做?这似乎是一个糟糕的想法,会导致各种错误,甚至除了这个错误。

标签: python-3.x namedtuple


【解决方案1】:

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

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

并获得一个没有 y 的 Point 和一个带有额外条目的 Point。

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

不要覆盖_make 以绕过此检查。您的对象与 namedtuple 的概念相距甚远 - 哎呀,与元组的概念相距甚远 - 您根本不应该使用 namedtuple 或任何元组子类。写一个普通的类就行了。

【讨论】:

    猜你喜欢
    • 2020-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-12
    • 2021-01-29
    • 2021-05-30
    • 2014-06-28
    相关资源
    最近更新 更多