我应该将Python列表子类化还是创建带有list作为属性的类?

时间:2014-08-15 14:39:16

标签: python list subclass extend

我需要一个可以收集大量对象的容器,并在容器的元素上提供一些报告功能。基本上,我希望能够做到:

magiclistobject = MagicList()
magiclistobject.report()  ### generates all my needed info about the list content

所以我想到了对普通列表进行子类化并添加了report()方法。这样,我就可以使用所有内置列表功能了。

class SubClassedList(list):
    def __init__(self):
        list.__init__(self)


    def report(self):      # forgive the silly example
        if 999 in self:
            print "999 Alert!"

相反,我也可以创建我自己的具有magiclist属性的类,但是如果我想使用以下命令进入列表,我必须创建新的方法来追加,扩展等等。

magiclistobject.append() # instead of magiclistobject.list.append()

我需要这样的东西(这似乎是多余的):

class MagicList():
    def __init__(self):
        self.list = []

    def append(self,element):
        self.list.append(element)

    def extend(self,element):
        self.list.extend(element)

# more list functionality as needed...

    def report(self):       
        if 999 in self.list:
            print "999 Alert!"

我认为对列表进行子类化将是一个明智的选择。但是this post here让它听起来像是禁忌。为什么呢?

2 个答案:

答案 0 :(得分:10)

扩展列表可能不好的一个原因是因为它将“MagicReport”对象与列表过于紧密地联系在一起。例如,Python列表支持以下方法:

append
count
extend
index
insert
pop
remove
reverse
sort

它还包含许多其他操作(添加,使用<>进行比较,切片等)。

您的'MagicReport'对象实际上想要支持的所有操作事项是什么?例如,以下是合法的Python:

b = [1, 2]
b *= 3
print b   # [1, 2, 1, 2, 1, 2]

这是一个非常人为的例子,但是如果你从'list'继承,你的'MagicReport'对象会做同样的事情,如果有人无意中做了这样的话。

另一个例子,如果你尝试切割MagicReport对象怎么办?

m = MagicReport()

# Add stuff to m

slice = m[2:3]
print type(slice)

您可能希望切片是另一个MagicReport对象,但它实际上是一个列表。您需要覆盖__getslice__以避免出现令人惊讶的行为,这有点痛苦。


这也使您更难更改MagicReport对象的实现。如果您最终需要进行更复杂的分析,那么通常可以将基础数据结构更改为更适合问题的结构。

如果你是列表的子类,可以通过提供新的appendextend等方法解决这个问题,这样你就不会改变界面,但是除非您仔细阅读整个代码库,否则您将无法确定实际使用哪种列表方法。但是,如果您使用合成并且只将列表作为字段并为您支持的操作创建方法,则您确切知道需要更改的内容。

我实际上遇到的情况与你最近的工作非常相似。我有一个对象,其中包含我首先在内部表示为列表的“事物”集合。随着项目要求的改变,我最终将对象更改为内部使用dict,自定义集合对象,最后是快速连续的OrderedDict。至少根据我的经验,组合使得更改某些内容的实现变得更容易,而不是继承。


话虽这么说,我认为扩展列表可能适用于你的'MagicReport'对象合法地只是名单中的列表的情况。如果你想要在每一种方式中使用MagicReport作为列表,并且不打算改变它的实现,那么它可能更方便子类列表并且只是完成它。

虽然在这种情况下,最好只使用一个列表并编写一个“报告”函数 - 我无法想象您需要多次报告列表的内容,并创建一个自定义对象一个仅用于此目的的自定义方法可能过度(尽管这显然取决于您正在尝试做什么)

答案 1 :(得分:5)

作为一般规则,每当你问自己时,我应该继承或拥有该类型的成员&#34;,选择不继承。这个经验法则被称为&#34;赞成组合而不是继承&#34;。

之所以如此,那就是:组合适合你想要使用另一个类的特征;如果其他代码需要使用您正在创建的类的其他类的功能,则继承是合适的。