简体   繁体   English

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

[英]Should I subclass Python list or create class with list as attribute?

I need a container that can collect a number of objects and provides some reporting functionality on the container's elements. 我需要一个可以收集大量对象的容器,并在容器的元素上提供一些报告功能。 Essentially, I'd like to be able to do: 基本上,我希望能够做到:

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

So I thought of subclassing the normal list and adding a report() method. 所以我想到了对普通列表进行子类化并添加了一个report()方法。 That way, I get to use all the built-in list functionality. 这样,我就可以使用所有内置列表功能。

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


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

Instead, I could also create my own class that has a magiclist attribute but I would then have to create new methods for appending, extending, etc., if I want to get to the list using: 相反,我也可以创建我自己的具有magiclist属性的类,但是如果我想使用以下命令到达列表,我将不得不创建新的方法来追加,扩展等。

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

I would need something like this (which seems redundant): 我需要这样的东西(这似乎是多余的):

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!"

I thought that subclassing the list would be a no-brainer. 我认为对列表进行子类化将是一个明智的选择。 But this post here makes it sounds like a no-no. 这篇文章让这听起来像是禁忌。 Why? 为什么?

One reason why extending list might be bad is since it ties together your 'MagicReport' object too closely to the list. 扩展列表可能不好的一个原因是因为它将“MagicReport”对象与列表过于紧密地联系在一起。 For example, a Python list supports the following methods: 例如,Python列表支持以下方法:

append
count
extend
index
insert
pop
remove
reverse
sort

It also contains a whole host of other operations (adding, comparisons using < and > , slicing, etc). 它还包含许多其他操作(添加,使用<>进行比较,切片等)。

Are all of those operations things that your 'MagicReport' object actually wants to support? 所有这些操作都是你的'MagicReport'对象真正想要支持的吗? For example, the following is legal Python: 例如,以下是合法的Python:

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

This is a pretty contrived example, but if you inherit from 'list', your 'MagicReport' object will do exactly the same thing if somebody inadvertently does something like this. 这是一个非常人为的例子,但是如果你从'list'继承,你的'MagicReport'对象会做同样的事情,如果有人无意中做了这样的事情。

As another example, what if you try slicing your MagicReport object? 另外一个例子,如果你尝试切割MagicReport对象怎么办?

m = MagicReport()

# Add stuff to m

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

You'd probably expect the slice to be another MagicReport object, but it's actually a list. 您可能希望切片是另一个MagicReport对象,但它实际上是一个列表。 You'd need to override __getslice__ in order to avoid surprising behavior, which is a bit of a pain. 你需要覆盖__getslice__以避免令人惊讶的行为,这有点痛苦。


It also makes it harder for you to change the implementation of your MagicReport object. 它还使您更难以更改MagicReport对象的实现。 If you end up needing to do more sophisticated analysis, it often helps to be able to change the underlying data structure into something more suited for the problem. 如果您最终需要进行更复杂的分析,那么通常可以将基础数据结构更改为更适合问题的结构。

If you subclass list, you could get around this problem by just providing new append , extend , etc methods so that you don't change the interface, but you won't have any clear way of determining which of the list methods are actually being used unless you read through the entire codebase. 如果你是list的子类,你可以通过提供新的appendextend等方法解决这个问题,这样就不会改变接口,但是你没有任何明确的方法来确定哪个列表方法实际上是除非你仔细阅读整个代码库,否则使用它。 However, if you use composition and just have a list as a field and create methods for the operations you support, you know exactly what needs to be changed. 但是,如果您使用合成并且仅将列表作为字段并为您支持的操作创建方法,则您确切知道需要更改的内容。

I actually ran into a scenario very similar to your at work recently. 我实际上遇到了一个与你最近的工作非常相似的场景。 I had an object which contained a collection of 'things' which I first internally represented as a list. 我有一个对象,其中包含我首先在内部表示为列表的“事物”集合。 As the requirements of the project changed, I ended up changing the object to internally use a dict, a custom collections object, then finally an OrderedDict in rapid succession. 随着项目要求的改变,我最终将对象更改为内部使用dict,自定义集合对象,最后是快速连续的OrderedDict。 At least in my experience, composition makes it much easier to change how something is implemented as opposed to inheritance. 至少根据我的经验,组合使得更改某些内容的实现变得更容易,而不是继承。


That being said, I think extending list might be ok in scenarios where your 'MagicReport' object is legitimately a list in all but name. 话虽这么说,我认为扩展列表可能适用于你的'MagicReport'对象合法地除了名称之外的所有列表。 If you do want to use MagicReport as a list in every single way, and don't plan on changing its implementation, then it just might be more convenient to subclass list and just be done with it. 如果你确实希望将MagicReport作为一个列表以单一方式使用,并且不打算改变它的实现,那么它可能更方便子类列表并且只是完成它。

Though in that case, it might be better to just use a list and write a 'report' function -- I can't imagine you needing to report the contents of the list more than once, and creating a custom object with a custom method just for that purpose might be overkill (though this obviously depends on what exactly you're trying to do) 虽然在这种情况下,最好只使用一个列表并编写一个“报告”函数 - 我无法想象您需要多次报告列表的内容,并使用自定义方法创建自定义对象只是为了这个目的可能是矫枉过正(虽然这显然取决于你究竟想做什么)

As a general rule, whenever you ask yourself "should I inherit or have a member of that type", choose not to inherit. 作为一般规则,每当你问自己“我应该继承或拥有该类型的成员”时,选择不继承。 This rule of thumb is known as "favour composition over inheritance". 这个经验法则被称为“赞成组合而不是继承”。

The reason why this is so is: composition is appropriate where you want to use features of another class; 这样做的原因是:组合适合你想要使用另一个类的特征; inheritance is appropriate if other code needs to use the features of the other class with the class you are creating. 如果其他代码需要使用您正在创建的类的其他类的功能,则继承是合适的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM