TypeError: list indices must be integers or slices, not str значит, что ты обратился к списку по строковому ключу — как к словарю. Индексом списка может быть только целое число или срез, строка "name" туда не подходит.
Почти всегда это случается при разборе JSON: json.loads() или response.json() вернули список словарей, а в коде написано data["name"] вместо data[0]["name"]. Само сообщение называет виновника: слово list в начале строки говорит, что внешний объект — список, а не словарь.
Что означает list indices must be integers or slices, not str?
Python сообщает, что слева от квадратных скобок стоит список, а внутри скобок оказалась строка. Список хранит элементы по порядковым номерам, поэтому доставать из него можно только числом (data[0]), отрицательным числом (data[-1]) или срезом (data[:3]). Ключи вроде "name" понимает словарь — у него другое устройство. Ошибка при обращении к списку по строке возникает мгновенно, ещё до того, как Python посмотрит, есть ли вообще такое поле.
import json
raw = '[{"name": "Мария", "score": 91}, {"name": "Игорь", "score": 78}]'
data = json.loads(raw)
print(data["name"])
Traceback (most recent call last):
File "app.py", line 6, in <module>
print(data["name"])
~~~~^^^^^^^^
TypeError: list indices must be integers or slices, not str
Самая полезная часть сообщения — первое слово. list indices означает, что объект перед скобками — список. Если бы там была строка, Python написал бы string indices, если кортеж — tuple indices. То есть Python уже сказал тебе, какого типа объект у тебя в руках; остаётся признать это и подстроить обращение.
Главная причина: JSON вернул список словарей, а не словарь
Внешний уровень JSON — это либо объект {...}, либо массив [...]. Объект превращается в словарь Python, массив — в список. Документация API обычно показывает один элемент ответа, а сервер отдаёт массив таких элементов, и вот тут ломается интуиция: путь из документации ["items"]["title"] перестаёт работать, потому что между ними стоит ещё один уровень — номер элемента.
response = {
"status": "ok",
"items": [
{"id": 17, "title": "Кофемолка", "price": 3990},
{"id": 24, "title": "Термокружка", "price": 1290},
],
}
print(response["items"]["title"])
Traceback (most recent call last):
File "app.py", line 9, in <module>
print(response["items"]["title"])
~~~~~~~~~~~~~~~~~^^^^^^^^^
TypeError: list indices must be integers or slices, not str
response["items"] — это уже список, и следующий шаг обязан быть числом или циклом:
response = {
"status": "ok",
"items": [{"title": "Кофемолка", "price": 3990}, {"title": "Термокружка", "price": 1290}],
}
print(response["items"][0]["title"])
print(sum(item["price"] for item in response["items"]))
Кофемолка
5280
Вот та же мысль в виде таблицы «было — стало». Слева — то, что лежит в переменной, справа — рабочее обращение к полю.
| Что в руках |
Не работает |
Работает |
Список словарей [{...}, {...}] |
data["name"] |
data[0]["name"] |
Словарь со списком внутри {"items": [...]} |
data["items"]["title"] |
data["items"][0]["title"] |
Строка с JSON '[{"name": ...}]' |
data[0]["name"] |
json.loads(data)[0]["name"] |
Строка из csv.reader ["17", "Кофемолка"] |
row["title"] |
row[1] или csv.DictReader |
Почему ошибка появляется после json.load() или requests?
Потому что списки на верхнем уровне ответа — норма для API: список товаров, список пользователей, список сообщений. Пока ты работаешь со своим словарём, руками набранным в коде, всё предсказуемо. Как только данные приходят снаружи — из requests, из json.load(f) или из выгрузки — форму задаёшь не ты, а тот, кто эти данные отдал. TypeError при разборе JSON почти всегда означает ровно это: структура на один уровень глубже, чем ты предполагал.
Тот же эффект даёт csv.reader: он возвращает не словари, а списки строк, и row["title"] падает с той же ошибкой. Если нужны имена колонок — бери csv.DictReader, он читает заголовок файла и делает из каждой строки словарь.
import csv
lines = ["id,title,price", "17,Кофемолка,3990", "24,Термокружка,1290"]
for row in csv.DictReader(lines):
print(row["title"], row["price"])
Кофемолка 3990
Термокружка 1290
DictReader принимает любой источник строк — и открытый файл, и список, как здесь, поэтому пример можно запустить как есть.
Как за одну строку понять, что у тебя в руках?
Не угадывай структуру — распечатай её. Три строки закрывают вопрос: type(data).__name__ покажет тип, len(data) — сколько там элементов, а list(data)[:5] — как выглядит первый кусок, не заливая терминал мегабайтом текста. Именно list(data)[:5], а не срез data[:1]: по словарю срез не работает и падает сам, а так одна и та же строка годится и для списка, и для словаря. Это надёжнее, чем печатать data целиком: в большом ответе глазами структуру не разобрать. Одно условие: этот print должен куда-то попасть. Если файл запускается двойным кликом, окно закроется раньше, чем ты успеешь прочитать и вывод, и traceback — чинится это способом запуска, разобранным в статье скрипт закрывается сразу после запуска.
import json
raw = '{"count": 2, "results": [{"name": "Мария"}, {"name": "Игорь"}]}'
data = json.loads(raw)
print(type(data).__name__)
print(list(data)[:5])
items = data["results"]
print(type(items).__name__, len(items))
print(items[0])
dict
['count', 'results']
list 2
{'name': 'Мария'}
Теперь видно всё: снаружи словарь с ключами count и results, внутри results — список из двух словарей. Правильный путь до имени — data["results"][0]["name"]. Если ответ API бывает то списком, то словарём, проверить это можно через isinstance (подробнее — в справочнике: проверка типов через isinstance):
def first_user(payload):
if isinstance(payload, list):
return payload[0]
return payload["results"][0]
print(first_user([{"name": "Мария"}]))
print(first_user({"results": [{"name": "Игорь"}]}))
{'name': 'Мария'}
{'name': 'Игорь'}
Чем эта ошибка отличается от string indices must be integers?
Это соседние сообщения об одной и той же путанице, но с разной причиной. list indices значит, что JSON уже разобран и перед тобой список — не хватает числового индекса. string indices must be integers, not 'str' значит, что разбора вообще не было: в переменной лежит строка с текстом JSON, и Python честно пытается взять из неё символ по номеру. Лечение разное: в первом случае добавь индекс, во втором — вызови json.loads().
text = '[{"name": "Мария", "score": 91}]'
print(text[0]["name"])
Traceback (most recent call last):
File "app.py", line 3, in <module>
print(text[0]["name"])
~~~~~~~^^^^^^^^
TypeError: string indices must be integers, not 'str'
Здесь text[0] вернул один символ [, и уже у символа спросили ключ "name". Достаточно заменить f.read() на json.load(f) — и структура станет списком словарей. Та же ошибка ловится при работе с requests: response.text — это строка, а разобранная структура приходит из response.json().
Второй частый источник string indices — перебор словаря. for user in data по словарю выдаёт ключи, то есть строки, и user["name"] внутри цикла падает. Значения даёт data.values(), пары — data.items().
Списки индексируются числами и срезами — и больше ничем
Целое число, отрицательное число, срез — полный список допустимых индексов. Больше ничего список не примет: ни строку, ни дробное число.
prices = [199, 349, 990, 1290]
print(prices[0])
print(prices[-1])
print(prices[1:3])
print(prices[::2])
199
1290
[349, 990]
[199, 990]
Дробный индекс даёт почти такое же сообщение, только с другим типом в конце:
prices = [199, 349, 990, 1290]
print(prices[len(prices) / 2])
Traceback (most recent call last):
File "app.py", line 2, in <module>
print(prices[len(prices) / 2])
~~~~~~^^^^^^^^^^^^^^^^^
TypeError: list indices must be integers or slices, not float
Обычный / в Python всегда возвращает float, даже когда делится нацело. Для середины списка нужен //: prices[len(prices) // 2] вернёт 990. Как устроены срезы во всех вариантах — в справочнике: срезы списка.
Есть и зеркальная ситуация — когда список пытаются использовать как ключ словаря или элемент множества. Тогда Python скажет TypeError: unhashable type: 'list'; разбор этой ошибки — в статье unhashable type: 'list' — что значит ошибка в Python.
Как перебрать список словарей и достать поле?
Список словарей перебирают циклом for, и переменная цикла на каждом шаге — целый словарь, к которому уже можно обращаться по ключу. Никаких индексов вручную считать не нужно. Если требуется собрать одно поле со всех элементов, короче написать списковое включение, а если нужен один элемент по условию — next() с генератором и значением по умолчанию.
data = [{"name": "Мария", "score": 91}, {"name": "Игорь", "score": 78}]
for user in data:
print(user["name"], user["score"])
names = [user["name"] for user in data]
print(names)
igor = next((u for u in data if u["name"] == "Игорь"), None)
print(igor)
Мария 91
Игорь 78
['Мария', 'Игорь']
{'name': 'Игорь', 'score': 78}
Потренировать ровно этот приём можно на бесплатной задаче Итог корзины со скидкой: на вход приходит список словарей с товарами, надо сложить поля и применить скидку. Осторожно с одним соседним багом: если внутри такого цикла ещё и удалять элементы, часть из них тихо проскочит мимо — механика разобрана в статье удаление из списка в цикле пропускает элементы.
Чем TypeError здесь отличается от KeyError и IndexError?
Три сообщения выглядят похоже, но отвечают на разные вопросы. TypeError — неверный тип индекса: строкой в список лезть нельзя в принципе. KeyError — тип верный, объект действительно словарь, но такого ключа в нём нет. IndexError — тип верный, объект действительно список, но он короче, чем запрошенный номер. По сути Python подсказывает, на каком именно шаге твоя модель данных разошлась с реальностью.
| Сообщение |
Что произошло |
Первый шаг |
TypeError: list indices must be integers or slices, not str |
список индексируют строкой |
добавь номер: data[0]["name"] |
TypeError: string indices must be integers, not 'str' |
JSON ещё не разобран, это строка |
json.loads(text) или response.json() |
KeyError: 'email' |
это словарь, но ключа нет |
посмотри list(user) или используй user.get("email") |
IndexError: list index out of range |
это список, но он короче |
проверь len(data) |
Мягкий доступ к полям словаря (get, значения по умолчанию, setdefault) собран в справочнике: доступ к словарю и значения по умолчанию.
Частые ошибки
- Копировать путь из документации API, не проверив форму ответа. В примере показан один объект, а сервер отдаёт массив. Фикс:
print(type(data).__name__, len(data)) перед первым обращением к полю.
- Брать
response.text вместо response.json(). В руках останется строка, и ты получишь string indices must be integers, а не list indices. Фикс: разбирать ответ через response.json() или json.loads(response.text).
- Читать файл через
f.read() вместо json.load(f). Та же строка вместо структуры, тот же результат.
- Перебирать словарь и ждать значений.
for user in data по словарю даёт ключи-строки. Фикс: data.values() или data.items().
- Индексировать список тем, что пришло из
input(). input() всегда возвращает строку, поэтому prices[n] падает. Фикс: prices[int(n)].
- Считать середину списка через
/. Деление даёт float, и сообщение меняется на not float. Фикс: //.
- Обращаться к строке
csv.reader по имени колонки. csv.reader отдаёт списки. Фикс: csv.DictReader или номер колонки.
Практика: разбери вложенный JSON в браузере
Теория закрепляется, только когда код запускается. На Python Arena задачи проверяются скрытыми тестами прямо в браузере, ставить ничего не нужно. Начни со списка словарей: Сложи одинаковые товары — там надо пройти по списку пар и собрать словарь-сводку, ровно та операция, на которой чаще всего и возникает эта ошибка. Дальше можно взять весь бесплатный блок задач на словари: задачи по словарям.
Как читать и писать JSON без сюрпризов, включая json.dump и json.load, показано в справочнике: сериализация JSON. И если при сохранении файла русские буквы вдруг превратились в последовательности вида \u041c\u0430\u0448\u0430, это не поломка — разбор в статье JSON в Python: русские буквы превратились в коды.
Мини-резюме
- Сообщение
list indices must be integers… значит: в квадратных скобках у списка стоит строка, а нужен номер или срез.
- Первое слово сообщения — диагноз:
list indices — у тебя список, string indices — у тебя ещё строка.
- Главный источник — JSON: массив на верхнем уровне превращается в список словарей, поэтому
data["name"] становится data[0]["name"].
- Перед первым обращением к полю печатай
type(data).__name__, len(data) и list(data)[:5] — это дешевле, чем угадывать.
string indices must be integers, not 'str' — это забытый json.loads() или response.json(), а не забытый индекс.
- Список словарей перебирают циклом
for: переменная цикла уже словарь, к ней можно обращаться по ключу.
TypeError — неверный тип индекса, KeyError — нет такого ключа, IndexError — список короче запрошенного номера.
TypeError: list indices must be integers or slices, not strзначит, что ты обратился к списку по строковому ключу — как к словарю. Индексом списка может быть только целое число или срез, строка"name"туда не подходит.Почти всегда это случается при разборе JSON:
json.loads()илиresponse.json()вернули список словарей, а в коде написаноdata["name"]вместоdata[0]["name"]. Само сообщение называет виновника: словоlistв начале строки говорит, что внешний объект — список, а не словарь.Что означает list indices must be integers or slices, not str?
Python сообщает, что слева от квадратных скобок стоит список, а внутри скобок оказалась строка. Список хранит элементы по порядковым номерам, поэтому доставать из него можно только числом (
data[0]), отрицательным числом (data[-1]) или срезом (data[:3]). Ключи вроде"name"понимает словарь — у него другое устройство. Ошибка при обращении к списку по строке возникает мгновенно, ещё до того, как Python посмотрит, есть ли вообще такое поле.import json raw = '[{"name": "Мария", "score": 91}, {"name": "Игорь", "score": 78}]' data = json.loads(raw) print(data["name"])Самая полезная часть сообщения — первое слово.
list indicesозначает, что объект перед скобками — список. Если бы там была строка, Python написал быstring indices, если кортеж —tuple indices. То есть Python уже сказал тебе, какого типа объект у тебя в руках; остаётся признать это и подстроить обращение.Главная причина: JSON вернул список словарей, а не словарь
Внешний уровень JSON — это либо объект
{...}, либо массив[...]. Объект превращается в словарь Python, массив — в список. Документация API обычно показывает один элемент ответа, а сервер отдаёт массив таких элементов, и вот тут ломается интуиция: путь из документации["items"]["title"]перестаёт работать, потому что между ними стоит ещё один уровень — номер элемента.response = { "status": "ok", "items": [ {"id": 17, "title": "Кофемолка", "price": 3990}, {"id": 24, "title": "Термокружка", "price": 1290}, ], } print(response["items"]["title"])response["items"]— это уже список, и следующий шаг обязан быть числом или циклом:response = { "status": "ok", "items": [{"title": "Кофемолка", "price": 3990}, {"title": "Термокружка", "price": 1290}], } print(response["items"][0]["title"]) print(sum(item["price"] for item in response["items"]))Вот та же мысль в виде таблицы «было — стало». Слева — то, что лежит в переменной, справа — рабочее обращение к полю.
[{...}, {...}]data["name"]data[0]["name"]{"items": [...]}data["items"]["title"]data["items"][0]["title"]'[{"name": ...}]'data[0]["name"]json.loads(data)[0]["name"]csv.reader["17", "Кофемолка"]row["title"]row[1]илиcsv.DictReaderПочему ошибка появляется после json.load() или requests?
Потому что списки на верхнем уровне ответа — норма для API: список товаров, список пользователей, список сообщений. Пока ты работаешь со своим словарём, руками набранным в коде, всё предсказуемо. Как только данные приходят снаружи — из
requests, изjson.load(f)или из выгрузки — форму задаёшь не ты, а тот, кто эти данные отдал. TypeError при разборе JSON почти всегда означает ровно это: структура на один уровень глубже, чем ты предполагал.Тот же эффект даёт
csv.reader: он возвращает не словари, а списки строк, иrow["title"]падает с той же ошибкой. Если нужны имена колонок — бериcsv.DictReader, он читает заголовок файла и делает из каждой строки словарь.import csv lines = ["id,title,price", "17,Кофемолка,3990", "24,Термокружка,1290"] for row in csv.DictReader(lines): print(row["title"], row["price"])DictReaderпринимает любой источник строк — и открытый файл, и список, как здесь, поэтому пример можно запустить как есть.Как за одну строку понять, что у тебя в руках?
Не угадывай структуру — распечатай её. Три строки закрывают вопрос:
type(data).__name__покажет тип,len(data)— сколько там элементов, аlist(data)[:5]— как выглядит первый кусок, не заливая терминал мегабайтом текста. Именноlist(data)[:5], а не срезdata[:1]: по словарю срез не работает и падает сам, а так одна и та же строка годится и для списка, и для словаря. Это надёжнее, чем печататьdataцеликом: в большом ответе глазами структуру не разобрать. Одно условие: этотprintдолжен куда-то попасть. Если файл запускается двойным кликом, окно закроется раньше, чем ты успеешь прочитать и вывод, и traceback — чинится это способом запуска, разобранным в статье скрипт закрывается сразу после запуска.import json raw = '{"count": 2, "results": [{"name": "Мария"}, {"name": "Игорь"}]}' data = json.loads(raw) print(type(data).__name__) print(list(data)[:5]) items = data["results"] print(type(items).__name__, len(items)) print(items[0])Теперь видно всё: снаружи словарь с ключами
countиresults, внутриresults— список из двух словарей. Правильный путь до имени —data["results"][0]["name"]. Если ответ API бывает то списком, то словарём, проверить это можно черезisinstance(подробнее — в справочнике: проверка типов через isinstance):def first_user(payload): if isinstance(payload, list): return payload[0] return payload["results"][0] print(first_user([{"name": "Мария"}])) print(first_user({"results": [{"name": "Игорь"}]}))Чем эта ошибка отличается от string indices must be integers?
Это соседние сообщения об одной и той же путанице, но с разной причиной.
list indicesзначит, что JSON уже разобран и перед тобой список — не хватает числового индекса.string indices must be integers, not 'str'значит, что разбора вообще не было: в переменной лежит строка с текстом JSON, и Python честно пытается взять из неё символ по номеру. Лечение разное: в первом случае добавь индекс, во втором — вызовиjson.loads().text = '[{"name": "Мария", "score": 91}]' # это вернул f.read() или response.text print(text[0]["name"])Здесь
text[0]вернул один символ[, и уже у символа спросили ключ"name". Достаточно заменитьf.read()наjson.load(f)— и структура станет списком словарей. Та же ошибка ловится при работе сrequests:response.text— это строка, а разобранная структура приходит изresponse.json().Второй частый источник
string indices— перебор словаря.for user in dataпо словарю выдаёт ключи, то есть строки, иuser["name"]внутри цикла падает. Значения даётdata.values(), пары —data.items().Списки индексируются числами и срезами — и больше ничем
Целое число, отрицательное число, срез — полный список допустимых индексов. Больше ничего список не примет: ни строку, ни дробное число.
prices = [199, 349, 990, 1290] print(prices[0]) print(prices[-1]) print(prices[1:3]) print(prices[::2])Дробный индекс даёт почти такое же сообщение, только с другим типом в конце:
prices = [199, 349, 990, 1290] print(prices[len(prices) / 2])Обычный
/в Python всегда возвращаетfloat, даже когда делится нацело. Для середины списка нужен//:prices[len(prices) // 2]вернёт990. Как устроены срезы во всех вариантах — в справочнике: срезы списка.Есть и зеркальная ситуация — когда список пытаются использовать как ключ словаря или элемент множества. Тогда Python скажет
TypeError: unhashable type: 'list'; разбор этой ошибки — в статье unhashable type: 'list' — что значит ошибка в Python.Как перебрать список словарей и достать поле?
Список словарей перебирают циклом
for, и переменная цикла на каждом шаге — целый словарь, к которому уже можно обращаться по ключу. Никаких индексов вручную считать не нужно. Если требуется собрать одно поле со всех элементов, короче написать списковое включение, а если нужен один элемент по условию —next()с генератором и значением по умолчанию.data = [{"name": "Мария", "score": 91}, {"name": "Игорь", "score": 78}] for user in data: print(user["name"], user["score"]) names = [user["name"] for user in data] print(names) igor = next((u for u in data if u["name"] == "Игорь"), None) print(igor)Потренировать ровно этот приём можно на бесплатной задаче Итог корзины со скидкой: на вход приходит список словарей с товарами, надо сложить поля и применить скидку. Осторожно с одним соседним багом: если внутри такого цикла ещё и удалять элементы, часть из них тихо проскочит мимо — механика разобрана в статье удаление из списка в цикле пропускает элементы.
Чем TypeError здесь отличается от KeyError и IndexError?
Три сообщения выглядят похоже, но отвечают на разные вопросы.
TypeError— неверный тип индекса: строкой в список лезть нельзя в принципе.KeyError— тип верный, объект действительно словарь, но такого ключа в нём нет.IndexError— тип верный, объект действительно список, но он короче, чем запрошенный номер. По сути Python подсказывает, на каком именно шаге твоя модель данных разошлась с реальностью.TypeError: list indices must be integers or slices, not strdata[0]["name"]TypeError: string indices must be integers, not 'str'json.loads(text)илиresponse.json()KeyError: 'email'list(user)или используйuser.get("email")IndexError: list index out of rangelen(data)Мягкий доступ к полям словаря (
get, значения по умолчанию,setdefault) собран в справочнике: доступ к словарю и значения по умолчанию.Частые ошибки
print(type(data).__name__, len(data))перед первым обращением к полю.response.textвместоresponse.json(). В руках останется строка, и ты получишьstring indices must be integers, а неlist indices. Фикс: разбирать ответ черезresponse.json()илиjson.loads(response.text).f.read()вместоjson.load(f). Та же строка вместо структуры, тот же результат.for user in dataпо словарю даёт ключи-строки. Фикс:data.values()илиdata.items().input().input()всегда возвращает строку, поэтомуprices[n]падает. Фикс:prices[int(n)]./. Деление даётfloat, и сообщение меняется наnot float. Фикс://.csv.readerпо имени колонки.csv.readerотдаёт списки. Фикс:csv.DictReaderили номер колонки.Практика: разбери вложенный JSON в браузере
Теория закрепляется, только когда код запускается. На Python Arena задачи проверяются скрытыми тестами прямо в браузере, ставить ничего не нужно. Начни со списка словарей: Сложи одинаковые товары — там надо пройти по списку пар и собрать словарь-сводку, ровно та операция, на которой чаще всего и возникает эта ошибка. Дальше можно взять весь бесплатный блок задач на словари: задачи по словарям.
Как читать и писать JSON без сюрпризов, включая
json.dumpиjson.load, показано в справочнике: сериализация JSON. И если при сохранении файла русские буквы вдруг превратились в последовательности вида\u041c\u0430\u0448\u0430, это не поломка — разбор в статье JSON в Python: русские буквы превратились в коды.Мини-резюме
list indices must be integers…значит: в квадратных скобках у списка стоит строка, а нужен номер или срез.list indices— у тебя список,string indices— у тебя ещё строка.data["name"]становитсяdata[0]["name"].type(data).__name__,len(data)иlist(data)[:5]— это дешевле, чем угадывать.string indices must be integers, not 'str'— это забытыйjson.loads()илиresponse.json(), а не забытый индекс.for: переменная цикла уже словарь, к ней можно обращаться по ключу.TypeError— неверный тип индекса,KeyError— нет такого ключа,IndexError— список короче запрошенного номера.