为什么UserList子类似乎返回错误的切片类型?

时间:2014-12-18 17:30:26

标签: python collections subclassing

首先让我说我明白为什么子类list不能像你期望的那样工作(因为list是一个原始的内置类型,并且存在性能问题等等)。 AFAIK collections.UserList应该避免所有这些问题,并使子类化UserList完全像你期望的那样工作。例如,

class DumbList(list):
  pass
d1 = DumbList([1,2,3])
d2 = DumbList([4,5])
type(d1 + d2)

返回<class 'list'>,但

from collections import UserList
class DumbList(UserList):
  pass
d1 = DumbList([1,2,3])
d2 = DumbList([4,5])
type(d1 + d2)

按预期返回<class '__main__.DumbList'>。但是,即使使用UserList代替list,切片也会返回错误的类型:

class DumbList(UserList):
  pass
d = DumbList([1,2,3])
type(d[:2])

按预期返回<class 'list'>,而不是<class '__main__.DumbList'>

两个问题:

  • 这是为什么?
  • 如何使切片返回正确的类型?我能想到的最简单的事情就是:

class DumbList(UserList):
  def __getitem__(self, item):
    result = UserList.__getitem__(self, item)
    try:
      return self.__class__(result)
    except TypeError:
      return result

......但似乎这种锅炉板代码应该是不必要的。

1 个答案:

答案 0 :(得分:2)

在Python 2中,普通切片(没有步幅)将由__getslice__ method处理。 UserList实现早于向语言添加扩展切片(使用步幅),并且从未添加对它们的支持,请参阅issue 491398

Python 3实现只采用了Python 2版本,移至collections并删除了__getslice____setslice__,因为Python 3不再支持这些版本。

因此,__getitem__实施仍然只是:

def __getitem__(self, i): return self.data[i]

假设切片将在其他地方处理。

在Python 3中,通过将slice() built-in type传递给__getitem__来处理所有切片;只需测试该类型并将结果包装在type(self)调用中:

def __getitem__(self, i):
    res = self.data[i]
    return type(self)(res) if isinstance(i, slice) else res