3

这是从学习 Python 第 4 版中提取的。它的功能是使用列表子类化集合。但我不明白第 5 行list.__init__([]),请帮忙。即使我注释掉了这行代码,该代码仍然有效。为什么?

### file: setsubclass.py

class Set(list):
    def __init__(self, value = []):      # Constructor
        list.__init__([])                # Customizes list 
        self.concat(value)               # Copies mutable defaults

    def intersect(self, other):          # other is any sequence
        res = []                         # self is the subject
        for x in self:
            if x in other:               # Pick common items
                res.append(x)
        return Set(res)                  # Return a new Set

    def union(self, other):              # other is any sequence
        res = Set(self)                  # Copy me and my list
        res.concat(other)
        return res

    def concat(self, value):             # value: list, Set . . .
        for x in value:                  # Removes duplicates
            if not x in self:
                self.append(x)

    def __and__(self, other): return self.intersect(other)
    def __or__(self, other):  return self.union(other)
    def __repr__(self):       return 'Set:' + list.__repr__(self)

if __name__ == '__main__':
    x = Set([1,3,5,7])
    y = Set([2,1,4,5,6])
    print(x, y, len(x))
    print(x.intersect(y), y.union(x))
    print(x & y, x | y)
    x.reverse(); print(x)
    x
4

4 回答 4

6

书中的代码包含错误。我已经向 O'Reilly 的书籍提交了勘误表,您可以在此页面上与作者的回复一起阅读(搜索 982)。以下是他回应的一小段:

自第 2 版(2003 年——10 年前!)以来,该代码行显然已经出现在本书中,并且直到现在还没有被成千上万的读者注意到

该行缺少一个参数list.__init__([]),并且将其注释掉没有任何区别,只是稍微加快了您的程序。这是更正后的行:

        list.__init__(self, [])

当直接在类对象上调用不是静态方法或类方法的方法时,通常隐式的第一个参数self必须显式提供。如果像这样更正这条线,它将遵循安东尼斯在他的回答中谈到的良好做法。更正该行的另一种方法是使用super,这再次使self参数隐含。

        super(Set, self).__init__([])

书中的代码提供了一个不同[]空列表换句话说,整行都是死代码。self

要验证原始行没有效果很容易:暂时更改[]为非list.__init__([])空列表并观察结果Set实例不包含这些元素。然后插入self作为第一个参数,并观察列表中的项目现在已添加到Set实例中。

于 2013-08-01T08:51:39.757 回答
4

你是说这条线?

    list.__init__([])

当你重写__init__任何类型的方法时,最好总是调用继承的__init__方法;也就是__init__基类的方法。这样您就可以执行父类的初始化,并添加特定于子类的初始化代码。

即使您确信__init__父级的 什么都不做,您也应该遵循这种做法,以确保与未来版本的兼容性。

更新:正如劳里茨在另一个答案中解释的那样,这条线

    list.__init__([])

是错的。有关更多信息,请参阅他的答案和其他答案。

于 2013-08-01T08:15:44.233 回答
3

你的意思是list.__init__([])

它从子类初始化程序调用基本初始化程序。您的子类已将基类初始化程序替换为自己的初始化程序。

在这种情况下,注释 out 恰好起作用,因为未绑定的初始化程序是使用空列表而不是调用的self,因此是无操作的。这是代表作者的错误,很可能。

但通常最好确保在子类化时基类已经运行了它的初始化代码。这样,基类方法所依赖的所有内部数据结构都已正确设置。

于 2013-08-01T08:15:19.807 回答
1

这一行就像__init__在 Set 类中创建一个构造函数,调用它的基类构造函数。

你可能已经看到了:

class Set(list):
...   
def __init__(self, *args, **kwargs):
        super(Set, self).__init__(args, kwargs)
        # do something additional here
于 2013-08-01T12:17:18.677 回答