Problem "List index out of range"

Asked

Viewed 480 times

2

In a Python code I have several conditions that filter my data, and this data is connected to a position counter i.

But within one of these specific conditions I need to record the position of the die at that point to use it later. For this I tried to create a list and when this condition was met, the position would be inserted in the list.

I read a little bit about and understood that the problem is that I’m trying to iterate the list while reducing the value of i, but I don’t know how I can get around this situation.

As the code is a bit extensive I will put only a few parts to exemplify:

redline = []
.
.
.
for i in range(n-2):    
.
.
.
if dvaz[i-1] <= 0 and dvaz[i+2] >= dvaz[i]  and dvaz[i] >= 0.11:
            dvaz[i] = dvaz[i]
            redline.append(i)
.
.
.
fou.write('{};{};{};{}\n'.format(i, vaz[i], dvaz[i], redline[-1:])

redline is the list created to add the values of i, and in the case is only added in the above condition. However when it arrives in the output file, the value is correct, however it is within [], and I would need only the value to be written.

An example of how the output file is written this way:

1; 1; 1; [1]

Should I withdraw the :, error arises list index out of range.

  • What is n? Wouldn’t it be better to do len(lista)-2 within the range?

  • This was done earlier, but in another way, the len(vaz), and dvaz = diff(vaz)

  • An easy way to achieve what you want, perhaps not the most "pythonico", would be to replace redline[-1:] for str(redline[-1:]).replace('[','').replace(']','')

  • It worked for me, thank you! Could you put this as an answer, so I can mark it as answered? Thank you

  • 1

    redline[-1:] returns a list containing the last element of redline... if you only want the last element, without being a list, use redline[-1]... This will only work if the list is not empty... The logic of your program needs to be thought out in case the list is empty... You don’t need to skulk

1 answer

1

First you have to understand what is happening. Suppose I have this list:

redline = [ 0, 1, 2, 3, 4, 5 ]

It has 6 elements: the numbers from zero to 5. If I want to get a specific element, for example, what is at position 3, I do:

print(redline[3])

The code above prints 3, since it is the element of heading 3 (remembering that the first position is 0). Generally speaking, when using lista[indice], is returned the value that is in position indice.

But Python also allows you to use the syntax of slice to get "sub-lists" (or "list chunks"). For example:

print(redline[2:4]) # [2, 3]
print(redline[2:]) # [2, 3, 4, 5]
print(redline[:4]) # [0, 1, 2, 3]

In the case, [2:4] means that I will take the elements of index 2 and 3 (the initial value 2 is included, the final value 4 nay is included). Hence the result is the list [2, 3].

But values can be omitted. If I omit the end, it goes to the end of the list, so [2:] takes the element from position 2 to the end. Already [:4] picks from first position to index 3 (remember, the end value is not included).

Another detail is that Python also allows negative indexes, and the difference is that they start counting from the bottom of the list. So, redline[-1] takes the last element from the list (in this case, it is number 5), redline[-2] take the penultimate, and so on.

And if we use negative numbers on one slice, the operation is the same. In case, redline[-1:] means that the initial value is the last element, and as the final value has been omitted, it goes to the end of the list. That is, it returns a list containing only the last element.


Another difference is that if you try to access an index that does not exist, the list gives error. But if you use this index in a slice, nay:

redline = [ 0, 1, 2, 3, 4, 5 ]
print(redline[7:]) # retorna uma lista vazia
print(redline[7]) # IndexError

The list has 6 elements, so valid indices are between zero and 5. When doing redline[7:], the slice tries to take everything from position 7 to the end of the list. Since position 7 does not exist, the result is an empty list. But if we try to access the index directly (ie, redline[7], unused slice), he gives a IndexError: list index out of range.

In your case, redline[-1] would-be IndexError if the list is empty (because it has no element, it has no way to access the last one - nor any other).

Finally, when you print a list, it is shown with the delimiters [ and ], and the elements within them separated by a comma. In your case, as the list has only one element (only the last element), it is shown as [1].


That said, one way to solve your problem would be to simply get the last element, or "nothing" if the list is empty:

if redline:
    valor = redline[-1]
else:
    valor = ''

The if redline is a way to check that the list is not empty, since an empty list is considered a "false". So if the list is not empty, take the last element. If the list is empty, use an empty string (or any other value you want).

Another way to write the if above is:

valor = redline[-1] if redline else ''

Do not use any solution that involves turning the list into a string and then removing the brackets as it makes no sense. Even if "works" (i.e., "show what you want"), it is not ideal, basically for two reasons:

  • semantics: do you want a list element, or another list containing an element? From what I understand, you only want the last element on the list, so the correct way to get it is redline[-1]. Period. End. That’s it, nothing else. Okay, one if to see if the list is empty is recommended, if this situation is possible to occur (and from what we have seen, is).
  • much unnecessary work: it was suggested to create a string from the list returned by redline[-1:], and then make substitutions on this string to remove the brackets. Just because "works" doesn’t mean it’s the best way to do it. There’s no reason to go all the way around just to get a value you can get more easily by simply passing an index.

To another answer (which has been deleted) said that using regex is the most performative way to treat strings. Well, right away I’m telling you it’s not, but in any case we’re gonna do a quick test.

import timeit, re

# cria uma lista com os números de 0 a 199
redline = list(range(200))

def regex(redline):
    re.sub('[]/[]', "", str(redline[-1:]))

def replace(redline):
    str(redline[-1:]).replace('[','').replace(']','')

def indice(redline):
    str(redline[-1]) if redline else ''

n = 1000000
r = 5
print(timeit.repeat('regex(redline)', repeat=r, number=n, globals=globals()))
print(timeit.repeat('replace(redline)', repeat=r, number=n, globals=globals()))
print(timeit.repeat('indice(redline)', repeat=r, number=n, globals=globals()))

I used the module timeit to measure the running time of each of the options. Basically, I used regex suggested in the other reply, the replace suggested in the comments and the if/else suggested above (I even turn the element into a string, to make the comparison more "fair", but this would not be necessary, since when printing the value it would be converted to string).

Remembering that the results can vary a lot, because they depend on hardware, of other processes that were running at the same time, etc. That said, I use the method repeat, to run each of the functions 1 million times, and I do this 5 times. The result is a list containing the 5 times, for each function. On my machine, I got the following:

[2.0180632999999997, 2.1049371000000003, 2.0531832999999997, 1.9851327000000003, 2.0050208000000005]
[1.0190392999999993, 1.0544192, 0.9397298999999997, 1.1806303000000007, 0.9278553999999986]
[0.3631664000000008, 0.36723610000000306, 0.35816770000000275, 0.40702209999999894, 0.36287220000000175]

Note that the regex takes about 2 seconds, while the replace takes about 1 second. Already the if/else takes about 0.3 seconds. I ran several times on my machine, on another machine, on Repl.it and in the Ideone.com (in the latter I had to decrease the amount not to exceed the time allowed), and the results were similar: regex always takes longer than the replace, that always takes longer than if/else, and the relative difference between the solutions is similar in all cases. But honestly, this was already expected.

The slice you have to create another list. Then, str creates a string from that list, and each replace creates another string. With regex is worse because the regular expression itself has to be compiled (and converted to an internal structure), only to begin to make the substitutions (and the search done by regex uses other internal structures to obtain and store the pouch, and in the end the method sub also returns another string). That is, in addition to doing more things and taking longer to do so, it uses more memory. To say that this is more performative is a completely mistaken statement. To say that it is "the right form" is also a mistake, because the "right" depends on each case. And regex isn’t always the best solution.

Well, I did another test compiling the regex out of function:

reg = re.compile('[]/[]')
def regex(redline):
    reg.sub("", str(redline[-1:]))

This improves the times a bit (on my machine, it drops to about 1.2 seconds), but it is still slower than the other options.

Obviously, for small programs manipulating few data, the performance difference between the 3 solutions will be insignificant. But there remains the question of semantics, of making the code clearer and more correct, and of facilitating the understanding and maintenance of the code. If someone reads redline[-1] (Assuming you know Python), it’s pretty clear that that chunk of code takes the last element (if you have a if/else, it is clear that I am using a different value if the list is empty). Now if you have a regex, it can get more confusing ("Why did they convert the list to string and then do replace? Just to get the last element, or there’s something else I don’t understand?"). That is, in your specific case, as you only want to get the last element of the list, do not invent. Do not use slice, do not convert to string, do not replace, why you don’t need it for what you intend to do.


Nor will I enter the merit that the regex suggested in another answer not quite right, because it also replaces the bars (if they exist in the string), see. It makes no difference if the list only has numbers, but in any case, it takes too many characters, so it’s not 100% right. A regex to remove only square brackets would be thus (although this solution does not consider the case where the value itself is a string that has brackets, but we are already deviating too much from the question). But anyway, forget it and don’t use regex, because in this case you don’t need.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.