[Tutor] IndexError: list index out of range [ Program work fine , but gives this message , guidance requested ]
Brian van den Broek
broek at cc.umanitoba.ca
Sun Jan 8 22:03:52 CET 2006
John Joseph said unto the world upon 08/01/06 06:36 AM:
> --- Guillermo Fernandez Castellanos
> <guillermo.fernandez.castellanos at gmail.com> wrote:
>
>
>>Hi,
>>
>>Look at this:
>> >>> i=[1,2,3]
>> >>> i[len(i)]
>>Traceback (most recent call last):
>> File "<stdin>", line 1, in ?
>>IndexError: list index out of range
>>
>>
>>This means that I have tried to access an element
>>that is out of the
>>list, in this case i[3], that is, the 4th element of
>>the list.
>>
>>You are doing the same. You do a
>>j in range(len(seats))
>>and then you are accessing your list with seats[j]
>>and seats[j+1]. What
>>happens when j = len(seats)-1 and you call
>>seats[j+1]? :-)
>>
>>It happens indeed the same that in the example I
>>gave you first.
>>
>>Hope it helps. Good luck,
>>
>
>
> Thanks for the advice
> But I need to display the results
> What should I do in the for loop for this message
> to go
> my for loop is as
>
> for j in range(length) :
> if seats[j] <> seats[j+1]:
> print " The Seat", j , "And the seat",
> j+1 , "value, which are", seats[j]," and", seats[j+1],
> "are not same"
> else:
> print "The Seats ",j ,"And the seats",
> j+1, "Values,", seats[j], "and", seats[j+1], "are
> same"
> Thanks
> Joseph John
Joseph,
I think Guillermo has already given you the answer :-). But, I'll
make it more explicit.
I'll also do a rather more. My intent is to give you some idea of how
you can go about incrementally improving your code.
The code you posted is doing something like:
>>> a_list = [7,8,7]
>>> for index in range(len(a_list)):
print index, a_list[index]
print index + 1, len(a_list), a_list[index + 1]
0 7
1 3 8
1 8
2 3 7
2 7
3 3
Traceback (most recent call last):
File "<pyshell#97>", line 3, in -toplevel-
print index + 1, len(a_list), a_list[index + 1]
IndexError: list index out of range
Examine the output. When the iteration hits the last member of the
list (when it gets to the final element of range(len(a_list)), there
is no next element of the list, so the request to print the next
element doesn't work. The list length is 3, so I get the same
exception as if I'd done:
>>> a_list[3]
Traceback (most recent call last):
File "<pyshell#16>", line 1, in -toplevel-
a_list[3]
IndexError: list index out of range
directly. That suggests *not* asking for an iteration which uses up
the list -- leave a next element and the code will work:
>>> for index in range(len(a_list) - 1): # Note the difference
print index, a_list[index]
print index + 1, len(a_list), a_list[index + 1]
0 7
1 3 8
1 8
2 3 7
>>>
Of course, as you pointed out in your OP, right now, even with this
fix, you will only be testing for sequential duplicates. My test list
a_list = [7,8,7]
has dupes, but your approach won't find them.
Warning: I'm going to go into a number of issues that you might not
yet entirely understand. If I lose you, don't worry; just ask about it.
Let's both fix the requirement that the duplicates be sequential and
start putting the code into functions.
Consider this:
>>> def dupe_detector_1(sequence):
for item in sequence:
if sequence.count(item) > 1:
print "%s is duplicated!" %item
>>> dupe_detector_1(a_list)
7 is duplicated!
7 is duplicated!
>>>
OK, that might be better, as we can find non-sequential duplicates.
But, there are two problems. 1) The duplication warning is duplicated
:-) and 2)
>>> a_tuple = (4,3,4)
>>> dupe_detector_1(a_tuple)
Traceback (most recent call last):
File "<pyshell#47>", line 1, in -toplevel-
dupe_detector_1(a_tuple)
File "<pyshell#28>", line 3, in dupe_detector_1
if sequence.count(item) > 1:
AttributeError: 'tuple' object has no attribute 'count'
>>>
The function signature seems to indicate it will work for all
sequences, but it chokes on tuples.
The second problem is easy to fix:
>>> def dupe_detector_2(sequence):
# coerce to a list as lists have count methods.
sequence = list(sequence)
for item in sequence:
if sequence.count(item) > 1:
print "%s is duplicated!" %item
>>> dupe_detector_2((1,1,2))
1 is duplicated!
1 is duplicated!
>>>
1 down, 1 to go.
>>> def dupe_detector_3(sequence):
sequence = list(sequence)
seen_dupes = []
for item in sequence:
if item in seen_dupes:
# if it is there, we already know it is duplicated
continue
elif sequence.count(item) > 1:
print "%s is duplicated!" %item
seen_dupes.append(item)
>>> dupe_detector_3(a_list)
7 is duplicated!
>>>
That's much better :-)
There remain 2 things I'd want to do differently.
First, I'd separate the print logic from the detection logic:
>>> def dupe_detector_4(sequence):
'''returns a list of items in sequence that are duplicated'''
sequence = list(sequence)
seen_dupes = []
for item in sequence:
if item in seen_dupes:
# if it is there, we already know it is duplicated
continue
elif sequence.count(item) > 1:
seen_dupes.append(item)
return seen_dupes
>>> big_list = [1,42,3,4,2,3,54,6,7,3,45,6,4,32,43,5,32,4,4,42,3,42,3]
>>> dupes_in_big_list = dupe_detector_4(big_list)
>>> dupes_in_big_list
[42, 3, 4, 6, 32]
OK, you might say "fine, but I *wanted* an on-screen report". Let's do
that this way:
>>> def print_dupe_report(sequence):
'''prints a report of items in sequence that are duplicated'''
dupes = dupe_detector_4(sequence)
dupes.sort()
for d in dupes:
print "%s was duplicated" %d
>>> print_dupe_report(big_list)
3 was duplicated
4 was duplicated
6 was duplicated
32 was duplicated
42 was duplicated
>>>
The advantage of this is you might well want to do what
dupe_detector_4 does without the screen output. Making the function
have a smaller job makes it easier to reuse in other contexts.
The last thing is a bit subtle. Say I had a sequence of length 10**6
where most items were duplicated. dupe_detector_4 has to iterate over
the entire long sequence, and continually hit the continue clause as
it will have seen the item already.
This last version will fix that, too:
>>> def dupe_detector_5(sequence):
'''returns a list of items in sequence that are duplicated'''
sequence = list(sequence)
seen_dupes = []
for item in set(sequence): # Note the difference
if sequence.count(item) > 1:
seen_dupes.append(item)
return seen_dupes
>>> dupes_in_big_list = dupe_detector_5(big_list)
>>> dupes_in_big_list
[32, 3, 4, 6, 42]
>>>
We no longer need the continue clause, as converting to set ensures we
won't ever deal with the same item twice:
>>> set((1,1,1,2,2,2,2,2,3,4,4,5))
set([1, 2, 3, 4, 5])
And, preventing us from dealing with the same item twice is what makes
this better. To see that, consider:
>>> def iteration_comparison(sequence):
list_count = 0
set_count = 0
for i in list(sequence):
list_count += 1
for i in set(sequence):
set_count += 1
print list_count, set_count
>>> iteration_comparison((1,2,3,4))
4 4
>>> iteration_comparison((1,2,3,4,1,2,3,4,1,2,3,4,5,1,2,3,4,1,2,3,4))
21 5
>>>
For sequences with a lot of duplication, the set version (as in
dupe_detector_5) has to iterate over far fewer items. That'll be quicker.
OK, I hope most of that made sense.
Best,
Brian vdB
More information about the Tutor
mailing list