[Tutor] for x in myClass

Danny Yoo dyoo at hkn.eecs.berkeley.edu
Tue Sep 2 01:27:23 EDT 2003



On Tue, 2 Sep 2003, Brian Christopher Robinson wrote:

> A friend of mine had an assignment to write a program that would take an
> array and create another array containing indexes into the first array,
> then sort the second array according to the values in the first array.


Hi Brian,

It looks like you're doing this just out of curiosity, so I'm not too
concerned about this being a homework assignment.  But if you are doing
doing this for homework, please tell us --- we are prohibited from helping
much with homework.


> So, if you have the array:
>
> [1, 50, 0, 42]
>
> Your second array, called the tag array, would start out unsorted as:
>
> [0, 1, 2, 3]
>
> Then sorted it would be:
>
> [2, 0, 3, 1]

Ok, this looks like the sort is preceding in decending order.  Just wanted
to raise that point, since most references to "sorting" assume that we
want things in ascending (or more correctly, "nondecending") order.



> I wanted to create a TagArray (really TagList, but he's working in VB and
> I'm working in Python, oh well) class.  So I wrote this:
>
> class TagArray(list):
>      def __init__(self, array):
>          self.array = array
>          for i in range(len(array)):
>              self.append(i)

Those last two statements can also be written as:

     self.extend(range(len(array)))

extend() is a method that can append multiple elements at a time to a
list.  One line isn't much, but every bit helps.  *grin*



>      def __getitem__(self, k):
>          return self.array[list.__getitem__(self, k)]
>
>      def __contains__(self, k):
>          return k in self.array
>
> This correctly refers to the original array when you use the bracket
> notation to get an element.  However, if you loop through it, you get
> the indexes instead of the values they're pointing to in the original
> array.  I overrode __contains__ which is the "in" statement, but
> apparently that's not the same one used when you do a for loop.


Yes.  When we do:

     for x in L: ...                   ## Case 1

the 'in' is not the same 'in' as:

     x in L                            ## Case 2

and I think the confusion is that they share the same keyword in there.


In Case 1, you'll want to override the __iter__() method, which Python
uses to get an iterator when it does a 'for' loop:

###
>>> class testlist(list):
...     def __iter__(self):
...         print "Hi, I'm __iter__()!"
...         return list.__iter__(self)
...
>>> L = testlist([1,2,3])
>>> for x in L:
...     print x
...
Hi, I'm __iter__()!
1
2
3
###

Case 2 is the one that's handled by __contains__(), so that's why we can
see it when we do checks for in-clusion.



> Also, will this
    [overriding __setattr__ and __getattr__]
> cause the sort() method to work?


This is sorta iffy.  This inheritance approach feels fragile to me,
because it assumes that sort() will use __getitem__() and __setitem__()
during the sort()ing process.  There's no such guarantee that sort() will
do this, since I don't see any documentation about it in the Library
Reference:

    http://www.python.org/doc/lib/typesseq-mutable.html


But let's check this, just to see if it works:

###
>>> class test_sort(list):
...     def __getitem__(self, index):
...         print "I'm __getitem__()!"
...         return list.__getitem__(self, index)
...     def __setitem__(self, index, value):
...         print "I'm __setitem__()!"
...         list.__setitem__(self, index, value)
...
>>> l = test_sort([3,1,4,1,5,9,2,6])
>>> l.sort()
>>> l
[1, 1, 2, 3, 4, 5, 6, 9]
###

Nope, no go.  Looks like sort() does some kind of internal lookup that
doesn't use __getitem__() or __setitem__().


It might be better to avoid inheritance here.  The approach above tries to
augment the list class.  But it might be easier to try augmenting the list
instance instead.

For example, we can "decorate" the original list with indices.

###
def decorate_with_indices(L):
    "Destructively appends L's elements with index numbers."
    for i in range(len(L)):
        L[i] = (L[i], i)
###

This is something that isn't invisible or transparent --- it does
hideously mutate L --- but it's something that's very reversable.

Somehow, though, I don't think the original assigment anticipated that
sort() was available for use... *grin*


Good luck to you.




More information about the Tutor mailing list