我想在Python中编写一个自定义列表类(我们称之为MyCollection
),我最终可以调用它:
for x in myCollectionInstance:
#do something here
我该怎么做呢?是否有一些我必须扩展的类,或者是否有任何我必须覆盖的函数才能这样做?
答案 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__()
方法在内部填充列表,中断切片的添加。