how to do proper subclassing?

Peter Otten __peter__ at web.de
Tue Aug 3 13:59:54 EDT 2004


Jonas Koelker wrote:

> Hello there.
> 
> I'm new to this list (/me welcomes himself).
> 
> anyways, I'm having doubts about how to subclass
> properly. The big picture is that I want to handle
> permutations. As we all know, two popular notation
> forms exist: products of disjoint cycles ("(0 1 3)(2
> 4)") and 2*length matrices with the identity
> permutation as the first line ([0 1 2 3 4] over [1 3 4
> 0 2]). I find the latter more appropriate, since it's
> easy to store as a list (one can skip the first line,
> assuming it's _always_ the identity).
> 
> However, I want the permutation to act like a list, in
> all those cases where I don't override the operations.
> For example, assuming permfooation is the above
> permutation [1 3 4 0 2], permfooation[1:3] should
> return [3,4]. How is this done? Do I have to actually
> implement a slice-mechanism in the permutation class?

You already answered your question in the subject - you want a subclass of
list:

>>> class Perm(list):
...     def __init__(self, n):
...             list.__init__(self, range(n))
...     def __call__(self, i, k):
...             self[i], self[k] = self[k], self[i]
...             return self
...
>>> p = Perm(5)
>>> p
[0, 1, 2, 3, 4]
>>> p(0,4)(2,4)(0,1)(2,3)(1,2)
[1, 3, 4, 0, 2]
>>> p[1:2]
[3]
>>> p[1:3]
[3, 4]

Just in case you need it later, there is a special method for every
operator, e. g. __getitem__() for obj[sliceOrIndex], __mul__() for
multiplication etc. Write one for the class in question and it will grow
the corresponding operation. In the above example I have made the class
"callable" (i. e. its instances can behave like a function) by adding the
__call__() method. By returning self I ensure that you can chain these
calls. As you may know, this is not the way the list.sort() method behaves,
so this is probably bad style for methods mutating the original instance. 

As to __getitem__(), I think the easiest way to see what happens inside is
provided by the following snippet:

>>> class C:
...     def __getitem__(self, i):
...             return i
...
>>> C()[1]
1
>>> C()[1:2]
slice(1, 2, None)
>>> C()[1:2:3]
slice(1, 2, 3)

I. e. a typical minimal __getitem__() has to test whether it receives a
slice or an integer using isinstance().

Peter




More information about the Python-list mailing list