在子类化列表之前需要考虑什么?

19

最近我在处理一个编码问题时,有人看着我的代码说子类化list是不好的(我的问题与该类无关)。他说你不应该这么做,而且会带来许多副作用。这是真的吗?

我的问题是:Python中是否通常不建议子类化list,如果是,原因是什么?或者,在子类化list之前我应该考虑什么?


3
子类化列表本身并不是坏的,但有很多情况下它并不是你想要做的事情。除非你给出具体情况,否则我们无法回答这是否适用于此处。 - Glenn Maynard
4个回答

18

在编写类似于列表的类时,collections 模块中提供的 抽象基类,特别是 MutableSequence,非常有用。这些可用于 Python 2.6 及更高版本。

使用 ABCs,您可以实现类的“核心”功能,并提供逻辑上依赖于您定义内容的方法。

例如,在派生自 collections.Sequence 的类中实现 __getitem__ 就足以为您的类提供 __contains____iter__ 和其他方法。

您可能仍然希望使用包含的列表对象来完成繁重的工作。


1
自从Python 3.3+版本以后,它已经被移动到了collections.abc模块中。 - xmedeko

15
子类化list没有任何好处。覆盖其中的任何方法都不会被使用,因此可能会导致意外的错误。此外,在访问数据时,像self.append而不是self.foos.append或尤其是self[4]而不是self.foos[4]之类的操作通常很令人困惑。您可以通过仅子类化object来创建与列表完全相同的东西或(更好)与列表相似程度符合您的要求

3
如果我们想要向列表中添加一个属性该怎么办?例如,假设我们有mylist = [],我们想要像mylist.x = 3这样的东西。 - riza
1
在我想让我的状态包括一些列表和一个整数的情况下,我仍然可能会使用组合。对于这个问题,使用继承会导致编写时的混淆,使用时的混淆,并且真正带来的好处并不明显。@Selinap - Mike Graham
3
Python 3似乎不再适用于这种情况。它专门为此目的提供了collections.UserList。甚至建议现在可以从list中进行子类化:“现在直接从list进行子类化已经部分取代了这个类的需求;”。(我知道这是一个旧话题,上面的评论可能在写作时是正确的,只是想让现在看到这个问题的人清楚地了解这一点)。 - Claude
1
@Claude,有些人喜欢抽象的事实,即自从引入新式类以来,子类化内置类就成为可能(不会抛出错误),但事实仍然是它几乎没有用处。子类化列表具有大量未定义的行为,而真正的行为是无用的“没有虚拟”。在Python 2或Python 3中,子类化collections.MutableSequenceobject(2)/nothing(3)更加有用。 - Mike Graham
如果我想要一个带有几个自定义方法和检查的可迭代对象,那么我将要子类化list而不是重新实现多个list方法和迭代。如果UserList解决了这些问题,那就很好,但是OO编程到底怎么了,为什么每个人都想放弃子类化?而且,UserList文档现在说:“这个类的需求部分被直接从列表中进行子类化的能力所取代;然而,这个类可能更容易使用,因为底层列表可以作为属性访问。” - NeilG

11

我认为我首先要问自己的问题是:“我的新对象真的是一个列表吗?”。它是否像列表一样工作和表现?或者它是其他什么东西?

如果它是列表,那么所有标准的列表方法都应该是有意义的。

如果标准的列表方法不适用,那么你的对象应该包含一个列表,而不是成为一个列表。

在旧版本的Python(2.2?)中,从列表进行子类化在技术上存在各种问题,但在现代Python中没有问题。


4
只有在完全合理的情况下才继承,否则就使用组合。 - user395760
关于副作用,他有点过时了吗?数据包含在列表中,但是我需要使用许多其他特定的方法来完成许多事情。 - Edward Williams
是的,“is-a”关系使用继承,其他情况则使用组合。 - Spike Gronim

6
尼克是正确的。此外,虽然我不能针对Python发表言论,在其他面向对象的语言(如Java、Smalltalk)中,子类化列表是一个不好的想法。通常应避免使用继承,而应该使用委托-组合。
相反,您可以创建一个容器类并将调用委托给列表。容器类具有对列表的引用,甚至可以在自己的方法中公开列表的调用和返回。这增加了灵活性,并允许您稍后更改实现(使用不同的列表类型或数据结构)而不会破坏任何代码。如果您希望您的列表执行不同的列表操作,则您的容器可以执行此操作,并将纯列表用作简单的数据结构。
想象一下,如果您有47个不同的列表用途。您真的想要维护47个不同的子类吗?相反,您可以通过容器和接口来实现这一点。只需维护一个类并允许人们通过接口调用您的新方法,实现保持隐藏即可。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接