471,073 Members | 1,365 Online
Bytes | Software Development & Data Engineering Community
Post +

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 471,073 software developers and data experts.

Subclassing list the right way?

I want to subclass list so that each value in it is calculated at call
time. I had initially thought I could do that by defining my own
__getitem__, but 1) apparently that's deprecated (although I can't
find that; got a link?), and 2) it doesn't work.

For example:
>>class Foo(list):
.... def __getitem__(self, index):
.... return 5
....
>>a = Foo([1, 2, 3, 4, 5])
print a
[1, 2, 3, 4, 5]
>>print a[2:4]
[3, 4]
>>print a[3]
5

I first expected that to instead behave like:
>>print a
[5, 5, 5, 5, 5]
>>print a[2:4]
[5, 5]

Is there a "right" way to do this?
--
Kirk Strauser
Jun 27 '08 #1
2 983
On Apr 25, 4:03*pm, Kirk Strauser <kirk.strau...@gmail.comwrote:
I want to subclass list so that each value in it is calculated at call
time. *I had initially thought I could do that by defining my own
__getitem__, but 1) apparently that's deprecated (although I can't
find that; got a link?), and 2) it doesn't work.

For example:
>class Foo(list):

... * * def __getitem__(self, index):
... * * * * * * return 5
...>>a = Foo([1, 2, 3, 4, 5])
>print a
[1, 2, 3, 4, 5]
>print a[2:4]
[3, 4]
>print a[3]

5

I first expected that to instead behave like:
>print a
[5, 5, 5, 5, 5]
>print a[2:4]

[5, 5]

Is there a "right" way to do this?
--
Kirk Strauser
I'm not totally sure, but I think you have to implement __getslice__
as well, even if it is a deprecated magic function. My guess for this
is that list implements __getslice__ and when you ask for a slice,
Python checks for __getslice__ first, and then, if you don't have it,
calls __getitem__. And since list has it, it's what is called.

As for "print a", you have to override __str__ and __repr__ too.

As a side note, the second argument to __getitem__ is not index, it's
key, because this argument can either be an int index or a slice()
object.
Jun 27 '08 #2
On Apr 25, 7:03 am, Kirk Strauser <kirk.strau...@gmail.comwrote:
I want to subclass list so that each value in it is calculated at call
time. I had initially thought I could do that by defining my own
__getitem__, but 1) apparently that's deprecated (although I can't
find that; got a link?), and 2) it doesn't work.

For example:
>class Foo(list):

... def __getitem__(self, index):
... return 5
...>>a = Foo([1, 2, 3, 4, 5])
>print a
[1, 2, 3, 4, 5]
>print a[2:4]
[3, 4]
>print a[3]

5

I first expected that to instead behave like:
>print a
[5, 5, 5, 5, 5]
>print a[2:4]

[5, 5]

Is there a "right" way to do this?
--
Kirk Strauser
The built in types are very optimized. You can't make any assumptions
about how overriding methods on them will affect other method calls.

From the docs:

__getitem__( self, key)

Called to implement evaluation of self[key]. For sequence types, the
accepted keys should be integers and slice objects. Note that the
special interpretation of negative indexes (if the class wishes to
emulate a sequence type) is up to the __getitem__() method. If key is
of an inappropriate type, TypeError may be raised; if of a value
outside the set of indexes for the sequence (after any special
interpretation of negative values), IndexError should be raised. For
mapping types, if key is missing (not in the container), KeyError
should be raised. Note: for loops expect that an IndexError will be
raised for illegal indexes to allow proper detection of the end of the
sequence.

What that basicly says is that it affects what is returned by a[x].
>>class Foo(list):
.... def __getitem__(self, index):
.... return 5
....
>>a = Foo([1, 2, 3, 4, 5])
print a # a.__str__ called
[1, 2, 3, 4, 5]
>>print a[2:4] # a.__getslice__ called
[3, 4]
>>print a[3] # a.__getitem__ called
5

The _right_ way? Well, it depends on what you really want to do. If
you really want to calculate the value at call time but have it behave
exactly as a list in every way, then you will have to override pretty
much any method that deals with the values normally stored in the list
object. That means any method that either returns values stored in the
object, accepts as a parameter values stored in the object or uses the
values stored in the object in any way.

Methods that return values stored in the object:
__getitem__
__getslice__

pop

Methods that accept as a parameter values stored in the object:
__contains__
count
remove
index

Methods that use values stored in the object in some way:
__add__
__eq__
__ge__
__gt__
__iadd__
__imul__
__le__
__lt__
__mul__
__ne__
__reduce__
__reduce_ex__? I'm not sure about this one, I think it is for pickling
__repr__
__reversed__
__rmul__
__str__
__iter__
sort
reverse

I know that Py3.0 is going to implement Abstract Base Classes. I don't
know if that is going to clean up this behavior or not. So, you can
either write a lot of code or restrict the way you use lists to a
limited amount of functionality. There is a third option, which is to
calculate the values when storing. This might be a little easier, but
if you want to ensure that values returned by all methods are the same
type as your class you are back to overriding a lot of methods.

Matt
Jun 27 '08 #3

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

4 posts views Thread by GrelEns | last post: by
4 posts views Thread by Jonas Koelker | last post: by
3 posts views Thread by Emiliano Molina | last post: by
2 posts views Thread by BJörn Lindqvist | last post: by
2 posts views Thread by Uwe Mayer | last post: by
12 posts views Thread by Jane Austine | last post: by
5 posts views Thread by Pieter Linden | last post: by
5 posts views Thread by Mike Kent | last post: by
reply views Thread by leo001 | last post: by

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.