我如何在python中创建自定义列表类?

时间:2011-07-03 00:26:53

标签: python list

我想在Python中编写一个自定义列表类(我们称之为MyCollection),我最终可以调用它:

for x in myCollectionInstance:
    #do something here

我该怎么做呢?是否有一些我必须扩展的类,或者是否有任何我必须覆盖的函数才能这样做?

6 个答案:

答案 0 :(得分:27)

如果您的集合基本上像列表一样,那么您可以继承list

class MyCollection(list):
    def __init__(self, *args, **kwargs):
        super(MyCollection, self).__init__(args[0])

但是,如果您的主要愿望是您的集合支持迭代器协议,则只需提供__iter__方法:

class MyCollection(object):
    def __init__(self):
        self._data = [4, 8, 15, 16, 23, 42]

    def __iter__(self):
        for elem in self._data:
            yield elem

这允许您迭代MyCollection的任何实例。

答案 1 :(得分:23)

答案 2 :(得分:12)

我喜欢将MutableSequence作为recommended by Alex Martelli的子类。这对我很有用,特别是当我需要在我正在构建的列表之上添加自定义方法时。

from collections import MutableSequence

class MyList(MutableSequence):
    """A container for manipulating lists of hosts"""
    def __init__(self, data=None):
        """Initialize the class"""
        super(MyList, self).__init__()
        if (data is not None):
            self._list = list(data)
        else:
            self._list = list()

    def __repr__(self):
        return "<{0} {1}>".format(self.__class__.__name__, self._list)

    def __len__(self):
        """List length"""
        return len(self._list)

    def __getitem__(self, ii):
        """Get a list item"""
        return self._list[ii]

    def __delitem__(self, ii):
        """Delete an item"""
        del self._list[ii]

    def __setitem__(self, ii, val):
        # optional: self._acl_check(val)
        self._list[ii] = val

    def __str__(self):
        return str(self._list)

    def insert(self, ii, val):
        # optional: self._acl_check(val)
        self._list.insert(ii, val)

    def append(self, val):
        self.insert(len(self._list), val)

if __name__=='__main__':
    foo = MyList([1,2,3,4,5])
    foo.append(6)
    print foo  # <MyList [1, 2, 3, 4, 5, 6]>

答案 3 :(得分:4)

您可以扩展list类:

class MyList(list):

    def __init__(self, *args):
        super(MyList, self).__init__(args[0])
        # Do something with the other args (and potentially kwars)

使用示例:

a = MyList((1,2,3), 35, 22)
print(a)
for x in a:
    print(x)

预期产出:

[1, 2, 3]
1
2
3

答案 4 :(得分:0)

在Python 3中,我们有漂亮的collections.UserList([list])

  

模拟列表的类。实例的内容保存在   常规列表,可通过UserList的data属性访问   实例。实例的内容最初设置为的副本   列表,默认为空列表[]。列表可以是任何可迭代的   例如真实的Python列表或UserList对象。

     

除了支持可变方法和操作   序列中,UserList实例提供以下属性:data   一个真实的列表对象,用于存储UserList类的内容。

https://docs.python.org/3/library/collections.html#userlist-objects

答案 5 :(得分:0)

从头开始实施列表需要您实施 full container protocol

__len__()

__iter__()    __reversed__()

_getitem__()  __contains__()
__setitem__() __delitem__()

__eq__()      __ne__()       __gt__()
__lt__()      __ge__()       __le__()

__add__()     __radd__()     __iadd__()
__mul__()     __rmul__()     __imul__()

__str__()     __repr__()     __hash__

但是list的关键是它的只读协议, 由 collections.abc.Sequence 的 3 个方法捕获:

  • __len__()
  • __getitem__()
  • __iter__()

要查看它的实际效果,这里是一个 lazy 只读列表支持 由range instance (超级方便,因为它知道如何做切片体操), 任何物化值都存储在缓存中(例如字典):

import copy
from collections.abc import Sequence
from typing import Dict, Union

class LazyListView(Sequence):
    def __init__(self, length):
        self._range = range(length)
        self._cache: Dict[int, Value] = {}

    def __len__(self) -> int:
        return len(self._range)

    def __getitem__(self, ix: Union[int, slice]) -> Value:
        length = len(self)

        if isinstance(ix, slice):
            clone = copy.copy(self)
            clone._range = self._range[slice(*ix.indices(length))]  # slicing
            return clone
        else:
            if ix < 0:
                ix += len(self)  # negative indices count from the end
            if not (0 <= ix < length):
                raise IndexError(f"list index {ix} out of range [0, {length})")
            if ix not in self._cache:
                ...  # update cache
            return self._cache[ix]

    def __iter__(self) -> dict:
        for i, _row_ix in enumerate(self._range):
            yield self[i]

虽然上面的类仍然缺少 write-protocol 和所有其余的方法 像__eq__()__add__(),已经很实用了。

>>> alist = LazyListView(12)
>>> type(alist[3:])
LazyListView

一个好消息是切片保留了类,所以他们避免打破懒惰 并具体化元素(例如,通过编写适当的 repr() 方法)。

然而,这门课在简单的测试中仍然惨遭失败:

>>> alist == alist[:]
False

你必须实现 __eq__() 来解决这个问题,并使用类似的工具 functools.total_ordering() 实现 __gt__() 等:

from functools import total_ordering

@total_ordering
class LaxyListView
    def __eq__(self, other):
        if self is other:
            return True
        if len(self) != len(other):
            return False
        return all(a == b for a, b in zip(self, other)

    def __lt__(self, other):
        if self is other:
            return 0
        res = all(self < other for a, b in zip(self, other)
        if res:
            return len(self) < len(other)

但这确实是相当大的努力。

注意:如果您尝试绕过工作并继承list(而不是Sequence), 需要更多的修改,因为例如copy.copy() 现在也会尝试复制 底层 list 并最终调用 __iter__(),破坏懒惰; 此外,__add__() 方法在内部填充列表,中断切片的添加。