Python has no function for list index argument in __getitem()__
, __setitem()__
, __delitem()__
. Since I have been an R user for long time, it looks quite natural to utilize list index. I know there is pandas
or numpy
but it is cumbersome to change the type. AND IT NEEDS IMPORTING EXTRA PACKAGE!
>>> import numpy as np >>> lst = [1,2,3,4,5] >>> np.array(lst)[[0,3,4]] array([1, 4, 5])
Here is my proposal.
class list2(__builtins__.list): def __getitem__(self, x): if isinstance(x, __builtins__.list): #print(x) return [__builtins__.list.__getitem__(self, y) for y in x] else: return __builtins__.list.__getitem__(self,x) def __setitem__(self, index, elem): if isinstance(index, __builtins__.list): if isinstance(elem, __builtins__.list): for i,x in zip(index, elem): __builtins__.list.__setitem__(self, i, x) else: for i in index: __builtins__.list.__setitem__(self, i, elem) #self[i] = x else: __builtins__.list.__setitem__(self, index, elem) def __delitem__(self, index): if isinstance(index, __builtins__.list): for i in sorted(index, reverse = True): __builtins__.list.__delitem__(self, i) #self[i] = x else: __builtins__.list.__delitem__(self, index)
It seems to work okay.
>>> l = list2(['a', 'b', 'c', 'd', 'e']) >>> l[[2,3]] ['c', 'd'] >>> l[[2,3]]= ['-', '-'] >>> l ['a', 'b', '-', '-', 'e'] >>> del l[[0,3]] >>> l ['b', '-', 'e']
But is there any pitfall? or is there any part that needs to be improved?
Advertisement
Answer
The main pitfall with something like this is that it’s ‘astonishing’, which is generally frowned upon in Python – we expect the default list to behave like the default list.
Aside from that, your implementation mostly looks complete to me. There are maybe a couple of issues:
- What happens if we use
__setitem__
or__delitem__
and list the same index more than once? - What happens if we give
__setitem__
lists with mismatched lengths?
There’s one key thing that I think it would be worth changing: get rid of lists of indexes, and use tuples instead.
If you pass a comma separated list of indices to __getitem__
, these are actually parsed as a tuple!
>>> class Thing: ... def __getitem__(self, indices): ... print(indices) ... >>> t = Thing() >>> t[1, 2, 3] (1, 2, 3)
So, rather than using lists, expect tuples and ditch the double brackets.
In terms of other improvements, it’s probably better to get rid of the references to __builtins__
, and use super()
to access the functions from the default list implementation, and it’s a good idea to add a custom __repr__
so we don’t mix this up with the default list.
If you add all of these suggestions, it would look something like this (typing added for clarity):
from typing import Any, Tuple, Union class list2(list): def __getitem__(self, index: Union[int, Tuple[int], slice]) -> Any: if isinstance(index, (slice, int)): return super().__getitem__(index) return [super(list2, self).__getitem__(x) for x in index] def __setitem__(self, index: Union[int, Tuple[int], slice], elem: Any): if isinstance(index, (slice, int)): return super().__setitem__(index, elem) if len(index) != len(elem): raise ValueError("Number of elements in index does not match element.") for x, element in zip(index, elem): self[x] = element def __delitem__(self, index: Union[int, Tuple[int], slice]): if isinstance(index, (slice, int)): return super().__delitem__(index) for x in sorted(set(index), reverse=True): super().__delitem__(x) def __repr__(self) -> str: return f"list2({super().__repr__()})"