Option 1
Subclass collections.UserList(). This is a quick-and-dirty option... I mostly subclass UserList() these days...
Option 2
-> my original answer from ages ago <-
Subclass MutableSequence, as recommended by Alex Martelli.
If you want a fairly comprehensive implementation of a MutableSequence() list, you can take a look at the CPython collections.UserList() source.
I added notes about using an acl... if you want to restrict the list to only holding certain kinds of objects, you can use an acl method to optionally ensure that you're only maintaining certain object types in the MutableSequence() subclass.
from collections.abc import MutableSequence
class MyList(MutableSequence):
"""
An extensive user-defined wrapper around list objects.
Inspiration:
https://github.com/python/cpython/blob/208a7e957b812ad3b3733791845447677a704f3e/Lib/collections/__init__.py#L1174https://github.com/python/cpython/blob/208a7e957b812ad3b3733791845447677a704f3e/Lib/collections/__init__.py#L1174
"""
def __init__(self, initlist=None):
super().__init__()
self.data = []
if initlist is not None:
if isinstance(initlist, list):
self.data[:] = initlist
elif isinstance(initlist, MyList):
self.data[:] = initlist.data[:]
else:
self.data = list(initlist)
def __repr__(self):
return """<{} data: {}>""".format(self.__class__.__name__, repr(self.data))
def __lt__(self, other):
return self.data < self.__cast(other)
def __le__(self, other):
return self.data <= self.__cast(other)
def __eq__(self, other):
return self.data == self.__cast(other)
def __gt__(self, other):
return self.data > self.__cast(other)
def __ge__(self, other):
return self.data >= self.__cast(other)
def __cast(self, other):
return other.data if isinstance(other, MyList) else other
def __contains__(self, value):
return value in self.data
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
if isinstance(idx, slice):
return self.__class__(self.data[idx])
else:
return self.data[idx]
def __setitem__(self, idx, value):
# optional: self._acl_check(val)
self.data[idx] = value
def __delitem__(self, idx):
del self.data[idx]
def __add__(self, other):
if isinstance(other, MyList):
return self.__class__(self.data + other.data)
elif isinstance(other, type(self.data)):
return self.__class__(self.data + other)
return self.__class__(self.data + list(other))
def __radd__(self, other):
if isinstance(other, MyList):
return self.__class__(other.data + self.data)
elif isinstance(other, type(self.data)):
return self.__class__(other + self.data)
return self.__class__(list(other) + self.data)
def __iadd__(self, other):
if isinstance(other, MyList):
self.data += other.data
elif isinstance(other, type(self.data)):
self.data += other
else:
self.data += list(other)
return self
def __mul__(self, nn):
return self.__class__(self.data * nn)
__rmul__ = __mul__
def __imul__(self, nn):
self.data *= nn
return self
def __copy__(self):
inst = self.__class__.__new__(self.__class__)
inst.__dict__.update(self.__dict__)
# Create a copy and avoid triggering descriptors
inst.__dict__["data"] = self.__dict__["data"][:]
return inst
def append(self, value):
self.data.append(value)
def insert(self, idx, value):
self.data.insert(idx, value)
def pop(self, idx=-1):
return self.data.pop(idx)
def remove(self, value):
self.data.remove(value)
def clear(self):
self.data.clear()
def copy(self):
return self.__class__(self)
def count(self, value):
return self.data.count(value)
def index(self, idx, *args):
return self.data.index(idx, *args)
def reverse(self):
self.data.reverse()
def sort(self, /, *args, **kwds):
self.data.sort(*args, **kwds)
def extend(self, other):
if isinstance(other, MyList):
self.data.extend(other.data)
else:
self.data.extend(other)
if __name__=='__main__':
foo = MyList([1,2,3,4,5])
foo.append(6)
print(foo) # <MyList [1, 2, 3, 4, 5, 6]>
for idx, ii in enumerate(foo):
print("MyList[%s] = %s" % (idx, ii))