Python’s popularity can be attributed to a number of different factors. But one of its biggest selling points are the data types. Python provides users with a nearly unprecedented ability to create, manipulate, and store data. There are a lot of systems out there that provide as much flexibility and power. But not many combine the two so seamlessly.
However, that’s not to imply that this combination doesn’t cause the occasional issue. And that fact is readily apparent if you stumble on the “list indices must be integers or slices, not tuple”. This Python error occurs in large part because of the language’s flexibility and willingness to assume intent when items aren’t explicitly declared. But you’ll soon see exactly what causes the problem and how to fix it.
What Does the Error Mean?
The “list indices must be integers or slices, not tuple” error might look complex at first glance. However, the complexity is largely due to the error message’s reliance on technical terminology. It’s essentially just stating that you’ve attempted to access part of a list incorrectly. Specifically, by passing multiple positions instead of just one. This is often caused by a simple typo or formatting error such as using [0,4] rather than [0:4]. This can be especially easy to do if you’re using an IDE with autocompletion. However, there can be more complexity to this problem than you might suspect.
Tuples, Types, and Troubles
As previously noted, Python allows for a high level of flexibility when using its data types. We can show this with a few quick Python code samples. Consider the following sample.
planets = [‘Mercury’, ‘Venus’, ‘Earth’, ‘Mars’, ‘Jupiter’, ‘Saturn’, ‘Uranus’, ‘Neptune’]
print(planets[0])
In this example we create a list of planets in our solar system. Then we pass the [0] position to print. The script will print ‘Mercury’ to screen. This is because we passed an index value of 0 to a list data type. With 0, of course, acting as an integer. So we see the string located at 0 within the planets list. But what if we went deeper into this data type? Change line 2 to the following.
print(planets[0][0])
This statement refers to the 0 index value for the item located at 0 in the planets list. You could even try to go deeper into the list by asking for the [0][0][0] index. But Python’s interpreter knows that you can’t go deeper than a single character and will just return the last relevant item. In this example that’s ‘m’. But now try changing line 2 again.
print(planets[0,0])
This time around we get the “list indices must be integers or slices, not tuple” error. This is because [0,0] was interpreted as a tuple by Python’s interpreter. Passing a collection that isn’t modified can usually be safely seen as a tuple rather than a standard list. But the system can’t use one collection to parse another. As such, we receive the error. But there’s another way we might pass two separate values as a list index value. Try changing line 2 to the following.
print(planets[0][0:3])
Note that we’re accessing the initial value in the planets list. But we’re passing two variables as an index for the item in that initial list. And we’re using a colon instead of a comma to separate the values. This tells Python’s interpreter that we’re using a slice. We’re essentially slicing a string between the two index values. But the situation can become even more varied. What if we wanted to organize things a bit more by categorizing planets by whether or not they’re gas giants?
standardPlanets = [‘Mercury’, ‘Venus’, ‘Earth’, ‘Mars’]
gasGiants = [‘Neptune’, ‘Uranus’, ‘Saturn’, ‘Jupiter’]
planets = []
planets.append(standardPlanets)
planets.append(gasGiants)
print(planets[0][0])
We begin by creating three lists. Two, standardPlanets and gasGiants, are declared with a populated list. The third, planets, is empty on creation. We then append standardPlanets and gasGiants. The append could be used to create even more nested list items. If we were using a numpy array things could become even more complicated as we ventured into true multidimensional data structures. However, the issue with list indices and tuple indices will persist even through most 3rd party libraries. This is due to the fact that Python’s interpreter will always calculate passed variables the same way. It’s an intrinsic element of the language.
Note too that on line six we access a full planet name by passing two index values. The important point here is that we’re using the square brackets to separate those values. This tells the interpreter that we want to access the first position within the first list. With numbering, of course, starting at 0 instead of 1 in Python. To demonstrate just how versitle Python’s indexing is, put this line at the very end of the previous code sample.
print(planets[0][planets[0].index(‘Mercury’) +1])
We’re obviously working with a lot of variable positions in this one. But the most important point is that it demonstrates how we can both access string indices and use a string as an index value. Even to the level of using a string’s index value within a list to find the next iteration of the variable within it. This concept can even be used to grab a mean data value based on string matching rather than specified integer positions. Now change that new line to the following
print(planets[0][planets[0,0].index(‘Mercury’) +1])
We once again get the list indices error. But now change it one more time.
print(planets[0][planets[[0][0]].index(‘Mercury’) +1])
The important point to note here is that there’s a difference between [0][0] and [0,0]. The fact that Python’s interpreter needs to assume some essential elements of a passed value means that we have some minor limitations on what we’re able to use. And this leads into the methods we’ll use to actually fix the error.
How To Fix the Error
The ultimate issue with everything we’ve encountered so far comes down to typeerror list indices. Python’s interpreter is extremely good at guessing what we want to do with a specified variable. But what it can’t do is make an assumption about our intent when initially passing a value. When in doubt the interpreter defines by what it sees. And if the interpreter sees a collection being passed it will define it as a tuple unless specifically told otherwise. Again, this is due to the fact that the comma-separated values are unchanging upon initial declaration. As such, we simply need to put extra care into passing integers. Going back to the previous examples, let’s fix this problematic code.
planets = [‘Mercury’, ‘Venus’, ‘Earth’, ‘Mars’, ‘Jupiter’, ‘Saturn’, ‘Uranus’, ‘Neptune’]
print(planets[0,3])
We’re using a variation on the very first code sample. At this point, you should know exactly what to look for. We need to find an instance of numbers separated by a comma. In this case it’s the [0,3]. The next step is to figure out what the actual intent was. The two main options are multiple index positions or a slice. This generally needs to be measured on a case-by-case basis. We have two options. This is a slice.
print(planets[0:4])
And here we see what happens if we assume that the code was written to access indices.
print(planets[0][4])
Both of these examples run without producing the error message. However, we need to judge the output to determine which matches an assumed intent. The first example gives us the planets in our solar system that aren’t gas giants. The second gives us the letter ‘u’.
So it’s generally safe to assume that the original intent was to use the slice. This logic can be used when trying to fix code that you didn’t write. But of course, the best process when writing your own code is to continually test and fix these types of typo errors before they have a chance to grow.