Emulating the list.insert() method as a subclass of Python's list

Question:

I’m trying to build a class that inherits methods from Python’s list, but also does some additional things on top… it’s probably easier just to show code at this point…

class Host(object):
    """Emulate a virtual host attached to a physical interface"""
    def __init__(self):
    # Insert class properties here...
    pass

class HostList(list):
    """A container for managing lists of hosts"""
    def __init__(self):
        self = []

    def append(self, hostobj): 
        """append to the list...""" 
        if hostobj.__class__.__name__ == 'Host': 
            self.insert(len(self), hostobj)
        else:
            _classname = hostobj.__class__.__name__
            raise RuntimeError, "Cannot append a '%s' object to a HostList" % _classname

My problem is this… if I want to perform the same kind of object admission tests on insert() as I did on append(), I can’t find a way to code the new methods without to sacrificing support for one list expansion method (i.e. list.append(), list.insert(), or list.extend()). If I try to support them all, I wind up with recursive loops. What is the best way around this problem?

EDIT: I took the suggestion about subclassing collections.MutableSequence instead of Python’s list()

The resulting code… posting here in case, it helps someone…

from collections.abc import MutableSequence
class HostList(MutableSequence):
    """A container for manipulating lists of hosts"""
    def __init__(self, data):
        super(HostList, 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):
        # Good MutableSequence() implementation example in the cpython git repo:
        #    https://github.com/python/cpython/blob/208a7e957b812ad3b3733791845447677a704f3e/Lib/collections/__init__.py#L1215
        if isinstance(ii, slice):                                               
            return self.__class__(self._list[ii])                                
        else:                                                                   
            return self._list[ii] 

    def __delitem__(self, ii):
        """Delete an item"""
        # optional: self._acl_check(val)
        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):
        # optional: self._acl_check(val)
        self._list.append(val)
Asked By: Mike Pennington

||

Answers:

Use isinstance to check your objects to see if they’re instances of Host, and use super (e.g. super(HostList, self).insert(...)) to use the functionality of list, rather than reimplementing it yourself.

You should end up with something like:

def append(self, obj): 
    """append to the list..."""
    if not isinstance(obj, Host):
        raise RuntimeError, "Cannot append a '%s' object to a HostList" % obj.__class__.__name__
    super(HostList, self).append(obj)
Answered By: bradley.ayers

You could call list‘s method from your method, with super(). That way you don’t need to kludge it with the other methods.

Answered By: Sami J. Lehtinen

Unless if there’s a compelling reason to have your HostList container completely support the mutable container interface, I suggest using a has-a model rather than is-a. You’ll have the extra burden of ensuring type consistency with operations such as slicing (return a HostList container rather than a list).

Answered By: Jeremy Brown

If you can possibly avoid it, don’t inherit from builtin classes. (You can, but that doesn’t mean you should without a really compelling reason)

Those classes are optimised for speed, and that makes inheriting from them correctly quite tedious, since you end up having to override almost everything.

Inheriting from collections.MutableSequence instead lets you implement just a few essential methods, and get a robust fully featured implementation of the sequence API, without all the quirks and caveats that come with inheriting from list.

Answered By: ncoghlan
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.