通过Python中的子类扩展类型

时间:2013-08-01 08:11:04

标签: python

这是从Learning Python第4版中提取的。它的功能是使用list子类设置。但我不明白第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 个答案:

答案 0 :(得分:6)

书中的代码包含错误。我已经向O'Reilly的书籍提交了一份勘误表,您可以在this page(作者982)上阅读作者的回复。这是他回应的一小部分:

  

这个代码行显然已经出现在第二版(2003年 - 10年前)的书中,并且直到现在还没有被成千上万的读者注意到

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

        list.__init__(self, [])

当直接在类对象上调用非静态方法或类方法的方法时,必须显式提供通常隐式的第一个参数self。如果这条线被这样纠正,那么它将遵循Antonis在他的回答中谈到的良好做法。纠正这一行的另一种方法是使用super,这又会隐含self参数。

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

本书中的代码提供了一个不同的空列表([])作为self参数,这会导致 列表被初始化再一次,它很快就被垃圾收集了。换句话说,整行都是死代码。

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

答案 1 :(得分:4)

你的意思是这条线?

    list.__init__([])

当您覆盖任何类型的__init__方法时,总是调用继承的__init__方法是一种很好的做法;也就是说,基类的__init__方法。这样,您可以执行父类的初始化,并添加特定于子类的初始化代码。

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

更新:正如Lauritz在另一个答案中解释的那样,

    list.__init__([])

错了。请参阅他的答案和其他答案。

答案 2 :(得分:3)

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

它从子类初始化程序调用基本初始值设定项。您的子类已将替换基类初始值设定项及其自己的。

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

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

答案 3 :(得分:1)

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

您可能已经看过这个:

class Set(list):
...   
def __init__(self, *args, **kwargs):
        super(Set, self).__init__(args, kwargs)
        # do something additional here