Implementing __getitem__(), __setitem__(), and __delitem__()

When we implement the __getitem__(), __setitem__() and __delitem__() methods, we must work with two kinds of argument values: int and slice. This variant behavior requires two different type hints. The hints are provided using the @overload decorator.

When we overload the various sequence methods, we must handle the slice situation appropriately within the body of the method. This requires using the isinstance() function to discern whether a slice object or a simple int has been provided as an argument value.

Here is a __setitem__() method that works with slices:

@overload
def __setitem__(self, index: int, value: float) -> None: ...

@overload
def __setitem__(self, index: slice, value: Iterable[float]) -> None: ...

def __setitem__(self, index, value) -> None:
if isinstance(index, slice):
start, stop, step = index.indices(len(self))
olds = [self[i] for i in range(start, stop, step)]
super().__setitem__(index, value)
for x in olds:
self._rmv(x)
for x in value:
self._new(x)
else:
old = self[index]
super().__setitem__(index, value)
self._rmv(old)

The preceding method has two processing paths:

  • If the index is a slice object, we'll compute the start, stop, and step values. Then, we'll locate all the old values that will be removed. We can then invoke the superclass operation and fold in the new values that replaced the old values.
  • If the index is a simple int object, the old value is a single item, and the new value is also a single item.

Note that __setitem__() expected multiple type hints, written using the @overload descriptor. The __delitem__() definition, on the other hand, relies on Union[int, slice], instead of two overloaded definitions.

Here's the __delitem__() method, which works with either a slice or an integer:

def __delitem__(self, index: Union[int, slice]) -> None:
# Index may be a single integer, or a slice
if isinstance(index, slice):
start, stop, step = index.indices(len(self))
olds = [self[i] for i in range(start, stop, step)]
super().__delitem__(index)
for x in olds:
self._rmv(x)
else:
old = self[index]
super().__delitem__(index)
self._rmv(old)

The preceding code, too, expands the slice to determine what values could be removed. If the index is a simple integer, then just one value is removed.

When we introduce proper slice processing to our StatsList2 class, we can create lists that do everything the base list class does, and also (rapidly) return the mean and standard deviation for the values that are currently in the list.

Note that these method functions will each create a temporary list object, olds; this involves some overhead that can be removed. As an exercise for the reader, it's helpful to rewrite the _rmv() function to eliminate the use of the olds variable.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset