Строки и списки: перевод туда-обратно
Строка ведёт себя почти как список символов, но с одним запретом — на изменение. Учимся свободно переводить строку в список и обратно и использовать силу обоих миров.
Редакция Питоники
В уроке 1 мы выяснили: строку нельзя изменить по индексу. Но у списков такого запрета нет — они изменяемые свободно. Отсюда практичный приём: нужно «изменить» строку — превратите её в список, сделайте правки, соберите обратно через join. Разбираем мост между двумя типами по кусочкам.
list: строка становится списком
list(строка) разбирает текст на отдельные символы. Получившийся список — обычный: его можно менять, добавлять в него элементы, считать len. Перебор строки циклом for работает и без list — прямо по символам.
letters = list('каток')
print(letters)
for ch in 'окт':
print(ch)
['к', 'а', 'т', 'о', 'к'] о к т
join: сборка обратно
''.join(список) склеивает символы без разделителя — строка собрана. С разделителем — вставляет его между элементами: ', '.join делает читаемый перечень. Пустой разделитель и join с запятой — два самых частых вызова метода.
letters = ['к', 'о', 'т']
print(''.join(letters))
print(', '.join(['Анна', 'Олег', 'Ира']))
кот Анна, Олег, Ира
Замена символа через список
Теперь обход неизменяемости из урока 1 в полный рост: превращаем строку в список, меняем символ по индексу (списку это можно), собираем обратно. Три строки — и «изменённая» строка готова.
word = 'кот'
chars = list(word)
chars[0] = 'р'
print(''.join(chars))
рот
sorted: буквы по порядку
sorted(строка) сортирует символы и возвращает СПИСОК (не строку!) — классическая ловушка. Строкой результат делает join. Побочный эффект сортировки — проверка анаграмм: два слова анаграммы, если их отсортированные буквы равны.
print(sorted('кот'))
print(''.join(sorted('кот')))
print(''.join(sorted('ток')))
print(sorted('кот') == sorted('ток'))
['к', 'о', 'т'] кот кот True
| Операция | Код | Результат |
|---|---|---|
| Строка → список | list('кот') | ['к', 'о', 'т'] |
| Список → строка | ''.join(['к', 'о', 'т']) | 'кот' |
| Сортировка | sorted('кот') | ['к', 'о', 'т'] (список!) |
| Правка символа | chars[0] = 'р' + join | новая строка 'рот' |
Строка и список — две проекции одной последовательности: строка неизменяема и оптимизирована под текст, список изменяем и оптимизирован под правки. Мост list/join стоит запомнить как единый приём.
Как превратить строку в список символов
Строка ведёт себя почти как список символов, но с одним запретом — на изменение. Всё остальное у неё есть: индексы, срезы, len, in, обход циклом. У списков такого запрета нет, они изменяемые свободно. Отсюда практичный приём: нужно «изменить» строку — превратите её в список, сделайте правки, соберите обратно.
letters = list('каток')
print(letters)
for ch in 'окт':
print(ch)
['к', 'а', 'т', 'о', 'к'] о к т
Обратите внимание: list('каток') даёт пять элементов, а не один. Перебор циклом по строке работает и без list — прямо по символам. Так что если вам нужно просто пройтись по символам, конвертация не требуется: она нужна только ради изменения.
join: сборка обратно
Обратная операция — join. Разделитель вызывается у строки, список передаётся аргументом: ''.join(chars) склеивает символы без зазоров, а ', '.join(items) делает читаемый перечень. Пустой разделитель и join с запятой — два самых частых вызова метода, о которых мы подробно говорили в уроке 10.
letters = ['к', 'о', 'т']
print(''.join(letters))
print(', '.join(['Анна', 'Олег', 'Ира']))
кот Анна, Олег, Ира
Замена символа через список
Теперь обход неизменяемости из урока 1 в полный рост: превращаем строку в список, меняем символ по индексу — списку это можно, — собираем обратно. Три строки, и «изменённая» строка готова.
word = 'кот'
chars = list(word)
chars[0] = 'р'
print(''.join(chars))
рот
Сравните с приёмом через срез: s[:0] + 'р' + s[1:] — тоже работает, но выглядит как заклинание. Через список читается понятнее: «сделай список, поменяй первый, собери обратно». Для одной замены правда привычнее replace или срез, а вот когда правок много и они в разных местах — выигрывает список.
sorted: буквы по порядку
sorted(строка) сортирует символы и возвращает СПИСОК, а не строку. Это классическая ловушка: забытый join поверх sorted — и вместо слова печатается список букв. Побочный эффект сортировки — проверка анаграмм: два слова анаграммы, если их отсортированные буквы равны.
print(sorted('кот'))
print(''.join(sorted('кот')))
print(''.join(sorted('ток')))
print(sorted('кот') == sorted('ток'))
['к', 'о', 'т'] кот кот True
Проверка анаграмм через sorted работает и для слов целиком: 'listen'.lower() == 'silent'.lower() даст True после сортировки букв. Но обратите внимание: sorted вернул список, а сравнили мы списки — это честно, списки сравниваются поэлементно. Строка после join тоже сравнилась бы, но список сравнивается быстрее: он хранит ссылки на уже существующие объекты, а не пересоздаёт символы.
| Задача | Как написать | Что получится |
|---|---|---|
| Строка → список символов | list('кот') | ['к', 'о', 'т'] |
| Список → строка | ''.join(chars) | 'кот' |
| Перебор символов | for ch in s: | по одному символу |
| Замена символа | chars[0] = 'р'; ''.join(chars) | 'рот' |
| Сортировка букв | sorted('кот') | список, не строка |
| Проверка анаграмм | sorted(a) == sorted(b) | True / False |
Ещё один мост между мирами — enumerate. Он даёт и позицию, и символ: for i, ch in enumerate(s). Когда нужно править символы по индексам и знать, где именно вы находитесь, это ровно то, что нужно. Альтернатива range(len(s)) работает, но enumerate считается идиоматичнее — и это тот случай, когда разница не в скорости, а в читаемости кода.
И последнее. list() принимает не только строки — это общий механизм превращения любой итерируемой последовательности в список. list(range(5)) даёт [0, 1, 2, 3, 4], list(generator) — список из того, что выдаст генератор. Со строками работает то же правило, и это ещё одна причина не путать list(s) с s.split(): первое разбивает на символы, второе — по разделителю.
Строки и списки: как выбрать правильный тип
Строки и списки выглядят похоже: оба поддерживают индексы, срезы, len, in и обход циклом. Разница одна, но она определяет всё: строка неизменяема, список — нет. Отсюда вытекает простое правило выбора.
| Что нужно | Тип данных | Почему |
|---|---|---|
| Данные пришли текстом | строка | нечего менять, всё уже в одном объекте |
| Нужно отсортировать на месте | список | у строк sort не работает вовсе |
| Нужно удалить элемент | список | у строк нет remove и pop |
| Нужно положить значение в ключ | строка | строки неизменяемы и безопасны как ключи |
| Нужно сравнивать как «один объект» | строка | неизменяемость даёт быстрое сравнение |
| Нужно собрать результат из частей | список + join | join склеит всё за один проход |
Отдельная строка про скорость сравнения. Строки Python при сравнении == сначала проверяют, совпадают ли объекты, и только потом сравнивают содержимое. Для неизменяемых строк это безопасно, и потому сравнение двух строк часто оказывается быстрее, чем сравнение двух списков. Но на коротких текстах разница не измерима — она становится заметной на больших объёмах и в горячих циклах.
И последнее, что связывает урок с остальным курсом. Неизменяемость строк — не ограничение, а защита. Когда вы передаёте строку в функцию, она не может измениться «по дороге», и две части программы не могут испортить данные друг другу. Списки такой гарантии не дают, и в коде с ними приходится быть внимательнее к копированию.
Практика: разбор строки в словарь
Соберём вместе всё, что урок дал: строку разбираем, чистим, ключи приводим к единому регистру и складываем в словарь. Это не учебный пример — такой разбор встречается в каждом разборе конфигурации, заголовка HTTP или строки параметров.
raw = 'Name=anna;City=Kazan;Age=30'
pairs = raw.split(';')
data = {}
for pair in pairs:
key, value = pair.split('=')
data[key.lower()] = value.strip()
print(data)
print(data['city'])
{'name': 'anna', 'city': 'Kazan', 'age': '30'}
KazanОбратите внимание на две нормализации. Ключ приводится к нижнему регистру — чтобы искать по нему можно было не думая о написании. Значение только чистится: «Kazan» и «kazan» — это, возможно, разные города, и решать должен бизнес, а не код. Второй момент: разделитель ; выбран не случайно — = в значениях встречается чаще, и если бы он был разделителем, строку пришлось бы резать аккуратнее.
И напоследок. Весь урок свёлся к одной мысли: строка — это значение, и чтобы из неё получить данные, её надо разобрать. split даёт части, strip чистит их, lower приводит к единому виду, join собирает обратно. Эти четыре метода закрывают почти все задачи разбора текста, с которыми сталкивается Python-разработчик.
Сначала предскажи ответ в голове — это главный навык программиста.
chars = list('мир')
chars.reverse()
print(''.join(chars))
print(', '.join(['а', 'б']))
chars = list('кот')
chars[0] = 'р'
print(''.join(chars))
1. Что вернёт list('ок')?
2. Как собрать из ['к', 'о', 'т'] строку 'кот'?
3. Что вернёт sorted('кот')?
4. Как проверить, что слова «кот» и «ток» — анаграммы?
5. Зачем переводить строку в список, если она и так последовательность?
6. Что вернёт list('ок')?
7. Как заменить первый символ строки на 'р'?
8. Что вернёт ''.join(list('кот'))?
Из слова 'свет' получите слово 'цвет': превратите строку в список, замените первый символ (индекс 0) на 'ц', соберите через join и напечатайте. Затем напечатайте буквы 'свет' по порядку алфавита одной строкой.
Что быстрее: replace или замена через список?
Для замены всех вхождений — replace: он написан на C и работает за один проход. Список выигрывает, когда правки разнородные и по индексам: одна конвертация вместо цепочки replace и срезов.
Как перебрать строку с индексами?
enumerate(s) — цикл for i, ch in enumerate(s) даёт и позицию, и символ. Или range(len(s)) — но enumerate считается идиоматичнее.
Что быстрее: replace или замена через список?
Для замены всех вхождений — replace: он написан на C и работает за один проход. Список выигрывает, когда правки разнородные и по индексам: одна конвертация вместо цепочки replace и срезов. Для одной замены проще срез или replace.
Почему sorted возвращает список, а не строку?
sorted — универсальная функция: она сортирует любой перебираемый объект и всегда возвращает список. Это единообразие ценнее удобства; для строк поверх добавьте ''.join.
Как перебрать строку с индексами?
Функцией enumerate: цикл for i, ch in enumerate(s) даёт и позицию, и символ. Или range(len(s)) — но enumerate считается идиоматичнее, и при разнице в скорости код читается понятнее.
Как проверить, что два слова — анаграммы?
Отсортировать буквы и сравнить: sorted('кот') == sorted('ток') даёт True. Для слов целиком не забудьте привести к одному регистру: sorted(a.lower()) == sorted(b.lower()).
Понравился урок? Сошлитесь на него
«В уроке 1 мы выяснили: строку нельзя изменить по индексу.»
Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.
Что дальше
Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.
Что читать дальше
Строки · Урок 2
Срезы в Python: s[start:stop:step]
Срез — способ вырезать из строки кусок: первые пять символов, последние три, каждый второй, строка наоборот. Разбираем s[start:stop:step] до автоматизма: индексы, границы, шаг.
Строки · Урок 19
Подводные камни строк
Пять ловушек, на которых спотыкается каждый: код «не работает», а причины не видно. Разбираем их на реальных примерах — и с готовыми решениями.
Похожие уроки по темам
Подобраны автоматически по пересечению тем и ключевых слов.
Циклы · Урок 12
Генератор списка в Python: цикл в одну строку
Цикл сбора списка — из трёх строк в одну: выражение, for, условие. Разбираем анатомию генератора списка, фильтр через if, превращение через if-else и границу, за которой однострочник вредит.
генератор списка pythonlist comprehension python
Множества · Урок 2
Создание множеств: {} и set() — литерал, список, строка
Два способа создать множество: литерал в фигурных скобках и set() от списка или строки. Пустые скобки — это словарь, поэтому пустое множество пишут set().
set от спискаset от строки
Строки · Урок 10
join: собрать строку из частей
split разбирает строку на части, join собирает обратно: разделитель, список строк — и одна готовая строка вместо десяти склеек плюсом. Разбираем синтаксис и главный вопрос: почему не плюс.
join pythonjoin в python