emulating sequences

Alex Martelli aleax at aleax.it
Fri Aug 31 10:31:36 EDT 2001


"John Hunter" <jdhunter at nitace.bsd.uchicago.edu> wrote in message
news:1r4rqo726z.fsf at video.bsd.uchicago.edu...
>
> I have a user defined type that I would like to behave like a
> sequence, eg
>
> results = MyType()
> for result in results:
>         blah
>
> Talking about special methods, Beazley in 'Essential Python' says that
> the following methods are used by objects that want to emulate
> sequence and mapping objects:
>
> __len__, __getitem__, __setitem__, __delitem__, __getslice__,
> __setslice__, __delslice__

That's rather dated: you shouldn't use the slice methods, but
rather let your item methods accept a slice-object as an
alternative to an integer -- it's more flexible.


> If I implement each of these for MyType, then can I get the desired
> behavior?

Definitely.

> If so, is there a subset that is sufficient just to get the
> 'in' behavior?

Yes, all you need for "for x in whatever:" is that whatever
implements __getitem__.  The for statement will call
whatever.__getitem__ with an argument of 0, then 1, then
2, and so on, until __getitem__ raises IndexError.


> Finally, if you have a sequence, what is the best way to get an index
> of the first match of a value in a find command, similar to the C++
>
>    i = find(s.begin(), s.end(), val)
>
> Is there such a function, and if so, what is the illegal value
> indicating failure to find?

You should emulate lists and strings, which implement an index
method -- to quote the online manual, "mutable sequences should
provide methods append(), count(), index(), insert(), pop(),
remove(), reverse() and sort(), like Python standard list objects".
On "failure to find", the method should raise ValueError.

If you have a sequence and *don't know* whether it implements
index or (like, say, a tuple) doesn't, you could use, e.g:

import sys
def findin(sequence, item):
    try: return sequence.find(item)
    except AttributeError:
        for anitem, anindex in zip(sequence, xrange(sys.maxint)):
            if anitem==item: return anindex
        else: raise ValueError

This carefully avoids using len(sequence), just in case the
sequence doesn't define a __len__ but rather signals its
termination only with a raise of IndexError.  If you don't
care about this peculiar case, then a simpler approach is:

def findin(sequence, item):
    try: return sequence.find(item)
    except AttributeError:
        for i in range(len(sequence)):
            if sequence[i]==item: return i
        else: raise ValueError


Alex






More information about the Python-list mailing list