Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overriding append method after inheriting from a Python List

I want to create a list that can only accept certain types. As such, I'm trying to inherit from a list in Python, and overriding the append() method like so:

class TypedList(list):
    def __init__(self, type):
        self.type = type

    def append(item)
        if not isinstance(item, type):
            raise TypeError, 'item is not of type %s' % type
        self.append(item)  #append the item to itself (the list)

This will of cause an infinite loop because the body of append() calls itself, but I'm not sure what to do other than using self.append(item).

How I should go about doing this?

like image 804
chaindriver Avatar asked Aug 15 '10 12:08

chaindriver


People also ask

Does Python append overwrite?

Bookmark this question. Show activity on this post. The first element of the list is overwritten.

How do you override an inheritance in Python?

Method overriding is thus a part of the inheritance mechanism. In Python method overriding occurs by simply defining in the child class a method with the same name of a method in the parent class.

How do you write an append function in Python?

append method in Python is to insert an item at the end of the list, but it doesn't return anything but None . In your code: y = x. append(9) : you running x. append(9) and put the result into y .


1 Answers

I want to create a list that can only accept certain types. As such, I'm trying to inherit from a list in Python

Not the best approach! Python lists have so many mutating methods that you'd have to be overriding a bunch (and would probably forget some).

Rather, wrap a list, inherit from collections.MutableSequence, and add your checks at the very few "choke point" methods on which MutableSequence relies to implement all others.

import collections

class TypedList(collections.MutableSequence):

    def __init__(self, oktypes, *args):
        self.oktypes = oktypes
        self.list = list()
        self.extend(list(args))

    def check(self, v):
        if not isinstance(v, self.oktypes):
            raise TypeError, v

    def __len__(self): return len(self.list)

    def __getitem__(self, i): return self.list[i]

    def __delitem__(self, i): del self.list[i]

    def __setitem__(self, i, v):
        self.check(v)
        self.list[i] = v

    def insert(self, i, v):
        self.check(v)
        self.list.insert(i, v)

    def __str__(self):
        return str(self.list)

The oktypes argument is normally a tuple of types that you want to allow, but it's OK to pass a single type there of course (and, by making that one type an abstract base class, ABC, you can easily perform any kind of type-checking of your choice that way -- but, that's a different issue).

Here's some example code using this class:

x = TypedList((str, unicode), 'foo', 'bar')
x.append('zap')
print x
x.append(23)

the output is:

['foo', 'bar', 'zap']
Traceback (most recent call last):
  File "tl.py", line 35, in <module>
    x.append(23)
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/_abcoll.py", line 556, in append
    self.insert(len(self), value)
  File "tl.py", line 25, in insert
    self.check(v)
  File "tl.py", line 12, in check
    raise TypeError, v
TypeError: 23

Note in particular that we have not overridden append -- yet append is there and behaves just as expected.

The not-so-secret behind that bit of magic is revealed in the traceback: _abcoll.py (the implementation module for the abstract base classes in the collections module), at line 556, implements append by calling our insert -- which we have, of course, properly overridden.

This "template method design pattern" (absolutely precious for all kinds of OOP -- look for my talks on design patterns on youtube and you'll find out why;-), among other advantages, gives us the "choke point effect" I mentioned earlier: by adding some checks at a very few methods that you must implement, you gain the advantage that those checks apply to all the other relevant methods (and mutable sequences in Python have a lot of those;-).

It's no surprise that we end up with a very powerful and classic design pattern "behind the scenes", because the whole idea behind this implementation strategy comes right out of the immortal classic "Design Patterns" book (whose authors are often collectively referred to as the gang of four";-): prefer object composition over inheritance. Inheritance (from concrete classes) is a very rigid coupling mechanism, full of "gotchas" as soon as you're trying to use it to do anything even just slightly outside its rigorous limits; composition is extremely flexible and useful, and inheritance from appropriate abstract classes can complete the picture very nicely.

Scott Meyers' excellent "Effective C++", item 33, puts it even more strongly: make non-leaf classes abstract. Since by "non-leaf" he means "any class that's ever inherited from", an equivalent phrasing would be "never inherit from a concrete class".

Scott is writing in a C++ context, of course, but Paul Haahr gives exactly the same advice for Java, phrased as Don't subclass concrete classes -- and I generally second it for Python, though I do favor the gang-of-four's softer phrasing, prefer composition over (concrete class) inheritance (but I understand that both Scott and Paul are often writing for an audience which needs very direct and strongly phrased advice, almost phrased as "commandments" rather than advice, not softer phrased one that they might too easily ignore in the name of their convenience;-).

like image 146
Alex Martelli Avatar answered Oct 11 '22 16:10

Alex Martelli