Why does Python log a SyntaxWarning saying “list indices must be integers or slices”?

Take this code, which we want to return a list of two breakfast orders:
def get_orders():
return [
["egg", "spam"]
["egg", "bacon", "spam"]
]
If we import it in Python 3.8+, we’ll see a warning message on line 3:
>>> import example
/.../example.py:3: SyntaxWarning: list indices must be integers or slices, not tuple; perhaps you missed a comma?
["egg", "spam"]
This is a new warning added in Python 3.8. From the release notes:
When a comma is missed in code such as[(10, 20) (30, 40)]
, the compiler displays aSyntaxWarning
with a helpful suggestion. This improves on just having aTypeError
indicating that the first tuple was not callable. (Contributed by Serhiy Storchaka in bpo-15248.)
Indeed, if we run the function, it raises a TypeError
:
>>> example.get_orders()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/.../example.py", line 3, in get_orders
["egg", "spam"]
TypeError: list indices must be integers or slices, not tuple
Python emits the SyntaxWarning
at import time because it can predict that the code will raise this TypeError
. So why does the error occur?
Some objects in Python are indexable, such as lists. (Indexing is a form of subscripting.) You index a list with the syntax: some_list[index_or_slice]
. This will fetch a single element from the list in some_list
if you use a number as an index, such as some_list[0]
, or a range of elements if you use a slice as the index, such as some_list[1:3]
.
Although it is rarely necessary, you can also spread the indexing syntax over multiple lines, for example:
some_list[index_or_slice]
If this happens within parentheses or square brackets, you can also put the square brackets on a new line entirely:
(some_list[index_or_slice])
This looks odd, and I don’t think it’s ever necessary in Python code, but it’s valid.
Our get_orders()
function follows this pattern, unintentionally. We could replace some_list
with ["egg", "spam"]
, and [index_or_slice]
with ["egg", "bacon", "spam"]
. So the second line, ["egg", "bacon", "spam"]
, is not interpreted as a second list, but as an index of the first list. The index_or_slice
in the index is a tuple of three elements, without parentheses: "egg", "bacon", "spam"
. Lists only support integers and slices for indexing, so this line raises a TypeError
.
The fix is to follow the SyntaxWarning
and add a comma after the first list:
def get_orders():
return [
["egg", "spam"],
["egg", "bacon", "spam"],
]
This comma separates the two lists, so our function can return them in the outer list, as intended.
Note there’s now also a comma after the second order list. You should form a habit to always end each item in a multi-line list with a comma, even when it’s the last one. Then you can never encounter the error.
Other Types
Python raises this SyntaxWarning
for more types than just lists. It also raises them for several indexable built-in types:
- Strings, including f-strings
- Container types -
tuple
,list
- List comprehensions
You can see this on the console, for example with an f-string:
>>> def example():
... return f"123"[1, 2]
...
<stdin>:2: SyntaxWarning: str indices must be integers or slices, not tuple; perhaps you missed a comma?
Make your development more pleasant with Boost Your Django DX.
One summary email a week, no spam, I pinky promise.
Related posts:
- Why does Python log a SyntaxWarning for ‘is’ with literals?
- Why does Python log a SyntaxWarning saying “object is not subscriptable”?
- Why does Python log a SyntaxWarning saying “assertion is always true”?
- Why does Python log a SyntaxWarning saying “object is not callable”?
Tags: python