在这种情况下如何正确覆盖__add__?

时间:2017-05-21 11:08:16

标签: python

Link用于构建和操作链表:

class Link:
    """A linked list with a first element and the rest."""
    empty = ()

    def __init__(self, first, rest=empty):
        assert rest is Link.empty or isinstance(rest, Link)
        self.first = first
        self.rest = rest

    def __getitem__(self, i):
        if i == 0:
            return self.first
        else:
            return self.rest[i-1]

    def __len__(self):
        return 1 + len(self.rest)

    def __repr__(self):
        """Return a string that would evaluate to self."""
        if self.rest is Link.empty:
            rest = ''
        else:
            rest = ', ' + repr(self.rest)
        return 'Link({0}{1})'.format(self.first, rest)

通过模仿__repr__我想实现__add__函数:

def __add__(self, other):
    if self is Link.empty:
        return other
    else:
        return Link(self.first, add(self.rest, other))

然而它不起作用并且给我这样的错误(隐藏了实际的文件路径):

>>> lst =  Link(3, Link(4, Link(5)))
>>> lst 
Link(3, Link(4, Link(5)))
>>> lst + lst
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "PATH_TO_THE_FILE", line 31, in __add__
    return Link(self.first, add(self.rest, other))
NameError: name 'add' is not defined

所以我将__add__的最后一行更改为:

return Link(self.first, self.rest.__add__(other))

这次错误变成了:

TypeError: can only concatenate tuple (not "Link") to tuple

然后我删除了班级中的__add__方法并尝试了另一种方式:

首先将以下函数添加到模块中:

def extend_link(s, t):
    if s is Link.empty:
        return t
    else:
        return Link(s.first, extend_link(s.rest, t))

并在终端:

>>> lst =  Link(3, Link(4, Link(5)))
>>> Link.__add__ = extend_link
>>> lst + lst
Link(3, Link(4, Link(5, Link(3, Link(4, Link(5))))))

为什么Link.__add__ = extend_link有效,但在课堂上覆盖__add__并不是?

PS:示例来自here

1 个答案:

答案 0 :(得分:0)

您遇到的第一个错误是由于add不存在。

您在班级中覆盖__add__,这与覆盖+运算符相对应。因此,add(self.rest, other)应该只是self.rest + other

+运算符的工作原理是查找左成员的__add__方法,如果存在则调用它。这就是self.rest.__add__(other)同样有效的原因。但是,重新定义__add__的原因是可以直接使用+,因此永远不应使用此名称调用__add__

现在,你得到的第二个错误有点棘手。它清楚地表明您正在尝试添加一个Link实例和一个未定义的tuple实例。一些调查让我意识到,self.rest一度等于empty。但empty定义为()tuple

对此最简单的解决方法是检查__add__定义是否其中一个成员等于empty,并返回正确的结果,没有调用{{ 1}}。这就是您在+函数中所做的。

更好的解决方法是构建您的extend_link,以使其成为empty个实例。