我需要一个可以收集大量对象的容器,并在容器的元素上提供一些报告功能。基本上,我希望能够做到:
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让它听起来像是禁忌。为什么呢?
答案 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对象的实现。如果您最终需要进行更复杂的分析,那么通常可以将基础数据结构更改为更适合问题的结构。
如果你是列表的子类,可以通过提供新的append
,extend
等方法解决这个问题,这样你就不会改变界面,但是除非您仔细阅读整个代码库,否则您将无法确定实际使用哪种列表方法。但是,如果您使用合成并且只将列表作为字段并为您支持的操作创建方法,则您确切知道需要更改的内容。
我实际上遇到的情况与你最近的工作非常相似。我有一个对象,其中包含我首先在内部表示为列表的“事物”集合。随着项目要求的改变,我最终将对象更改为内部使用dict,自定义集合对象,最后是快速连续的OrderedDict。至少根据我的经验,组合使得更改某些内容的实现变得更容易,而不是继承。
话虽这么说,我认为扩展列表可能适用于你的'MagicReport'对象合法地只是名单中的列表的情况。如果你做想要在每一种方式中使用MagicReport作为列表,并且不打算改变它的实现,那么它可能更方便子类列表并且只是完成它。
虽然在这种情况下,最好只使用一个列表并编写一个“报告”函数 - 我无法想象您需要多次报告列表的内容,并创建一个自定义对象一个仅用于此目的的自定义方法可能过度(尽管这显然取决于您正在尝试做什么)
答案 1 :(得分:5)
作为一般规则,每当你问自己时,我应该继承或拥有该类型的成员&#34;,选择不继承。这个经验法则被称为&#34;赞成组合而不是继承&#34;。
之所以如此,那就是:组合适合你想要使用另一个类的特征;如果其他代码需要使用您正在创建的类的其他类的功能,则继承是合适的。