Sitemap
Amazing Tips

Amazing Tips

"Hey everyone! I'm excited to share my thoughts, ideas, and experiences with you. If you're interested in amazing tips, follow me . Let's grow together!"

How To Implementing Custom Containers With Python

4 min readFeb 15, 2023

--

Press enter or click to view image in full size

To Implementing Custom Containers

Problem

You want to implement a custom class that mimics the behavior of a common built-in container type, such as a list or dictionary. However, you’re not entirely sure what methods need to be implemented to do it.

Solution

The collections library defines a variety of abstract base classes that are extremely useful when implementing custom container classes. To illustrate, suppose you want your class to support iteration. To do that, simply start by having it inherit from col lections.Iterable, as follows:

import collections
class A(collections.Iterable):
pass

The special feature about inheriting from collections.Iterable is that it ensures you implement all of the required special methods. If you don’t, you’ll get an error upon instantiation:

>>> a = A()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class A with abstract methods __iter__
>>>

To fix this error, simply give the class the required __iter__() method and implement it as desired (see Recipes 4.2 and 4.7). Other notable classes defined in collections include Sequence, MutableSequence, Mapping, MutableMapping, Set, and MutableSet. Many of these classes form hierarchies with increasing levels of functionality (e.g., one such hierarchy is Container, Itera ble, Sized, Sequence, and MutableSequence). Again, simply instantiate any of these classes to see what methods need to be implemented to make a custom container with that behavior:

>>> import collections
>>> collections.Sequence()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Sequence with abstract methods \
__getitem__, __len__
>>>

Here is a simple example of a class that implements the preceding methods to create a sequence where items are always stored in sorted order (it’s not a particularly efficient implementation, but it illustrates the general idea):

Get Amazing Tips’s stories in your inbox

Join Medium for free to get updates from this writer.

import collections
import bisect
class SortedItems(collections.Sequence):
def __init__(self, initial=None):
self._items = sorted(initial) if initial is None else []
# Required sequence methods
def __getitem__(self, index):
return self._items[index]
def __len__(self):
return len(self._items)
# Method for adding an item in the right location
def add(self, item):
bisect.insort(self._items, item)

Here’s an example of using this class:

>>> items = SortedItems([5, 1, 3])
>>> list(items)
[1, 3, 5]
>>> items[0]
1
>>> items[-1]
5
>>> items.add(2)
>>> list(items)
[1, 2, 3, 5]
>>> items.add(-10)
>>> list(items)
[-10, 1, 2, 3, 5]
>>> items[1:4]
[1, 2, 3]
>>> 3 in items
True
>>> len(items)
5
>>> for n in items:
... print(n)
...
-10
1
2
3
5
>>>

As you can see, instances of SortedItems behave exactly like a normal sequence and support all of the usual operations, including indexing, iteration, len(), containment (the in operator), and even slicing. As an aside, the bisect module used in this recipe is a convenient way to keep items in a list sorted. The bisect.insort() inserts an item into a list so that the list remains in order

Discussion

Inheriting from one of the abstract base classes in collections ensures that your cus‐ tom container implements all of the required methods expected of the container. How‐ ever, this inheritance also facilitates type checking. For example, your custom container will satisfy various type checks like this:

>>> items = SortedItems()
>>> import collections
>>> isinstance(items, collections.Iterable)
True
>>> isinstance(items, collections.Sequence)
True
>>> isinstance(items, collections.Container)
True
>>> isinstance(items, collections.Sized)
True
>>> isinstance(items, collections.Mapping)
False
>>>

Many of the abstract base classes in collections also provide default implementations of common container methods. To illustrate, suppose you have a class that inherits from collections.MutableSequence, like this:

class Items(collections.MutableSequence):
def __init__(self, initial=None):
self._items = list(initial) if initial is None else []
# Required sequence methods
def __getitem__(self, index):
print('Getting:', index)
return self._items[index]
def __setitem__(self, index, value):
print('Setting:', index, value)
self._items[index] = value
def __delitem__(self, index):
print('Deleting:', index)
del self._items[index]
def insert(self, index, value):
print('Inserting:', index, value)
self._items.insert(index, value)
def __len__(self):
print('Len')
return len(self._items)

If you create an instance of Items, you’ll find that it supports almost all of the core list methods (e.g., append(), remove(), count(), etc.). These methods are implemented in such a way that they only use the required ones. Here’s an interactive session that illus‐ trates this:

>>> a = Items([1, 2, 3])
>>> len(a)
Len
3
>>> a.append(4)
Len
Inserting: 3 4
>>> a.append(2)
Len
Inserting: 4 2
>>> a.count(2)
Getting: 0
Getting: 1
Getting: 2
Getting: 3
Getting: 4
Getting: 5
2
>>> a.remove(3)
Getting: 0
Getting: 1
Getting: 2
Deleting: 2
>>>

This recipe only provides a brief glimpse into Python’s abstract class functionality. The numbers module provides a similar collection of abstract classes related to numeric data types. See Recipe 8.12 for more information about making your own abstract base classes

Download The Complete Book Now And fine More Solved Problems

--

--

Amazing Tips
Amazing Tips

Written by Amazing Tips

"Hey everyone! I'm excited to share my thoughts, ideas, and experiences with you. If you're interested in amazing tips, follow me . Let's grow together!"