Справочник по Python · модуль 5 · строки
Глава 5.9
Объединение, очистка
и разделение строки
join() собирает строку из частей, strip() удаляет символы по краям, а partition() делит строку ровно в одном месте и всегда возвращает три части. У каждого метода своя граница работы — и своя ловушка.
- Категория
- Строковые методы
- Уровень
- Базовый → средний
- Связанные темы
- Списки, кортежи, распаковка
- Зачем это знать
- Собирать текст и разбирать записи с разделителем
§1Три границы
Три метода этой главы работают в трёх разных местах строки, и это единственное, что нужно запомнить о них сразу.
join() между частями: "a" + сеп + "b" + сеп + "c" strip() по краям: ␣␣␣текст␣␣␣ partition() в одной точке: до | разделитель | после
Все три возвращают новый результат и не меняют исходную строку — строки неизменяемы.
§2join(): сборка строки
join() собирает одну строку из набора частей, вставляя разделитель между ними.
words = ["изучаем", "методы", "строк"] sentence = " ".join(words) print(sentence)
изучаем методы строк
Разделитель вставляется между элементами, а не после каждого: для трёх слов его копий будет две. По краям строки его нет.
§3Почему метод у разделителя
Запись " ".join(words) поначалу выглядит вывернутой наизнанку: логичнее ждать words.join(" "). Причина в том, что join() — метод строки, а соединять он умеет любую коллекцию.
separator = ", " print(separator.join(["a", "b"])) print(separator.join(("c", "d"))) print(separator.join("xyz"))
a, b c, d x, y, z
Будь join() методом списка, его пришлось бы отдельно определять для кортежа, множества и всего остального. Третья строка показывает побочный эффект: строка тоже коллекция, и она разберётся на символы.
§4Только строки
Все элементы должны быть строками. Числа join() сам не преобразует.
print(", ".join([1, 2]))
TypeError: sequence item 0: expected str instance, int found
Числа переводят в строки заранее — включением из главы 4.10 или функцией map() из главы 6.8.
numbers = [10, 20, 30] print(", ".join(str(number) for number in numbers))
10, 20, 30
§5Пустой разделитель
Разделитель может быть пустым — тогда части просто склеиваются.
letters = ["P", "y", "t", "h", "o", "n"] print("".join(letters))
Python
Эта форма уже встречалась в главе 5.1, когда список кодов превращали обратно в текст.
§6join() вместо склейки в цикле
Ту же строку можно собрать сложением в цикле. Результат совпадёт, но способ хуже.
result = ""
for part in parts:
result += part на каждом шаге создаётся новая строка
result = "".join(parts) строка создаётся один раз
Причина в неизменяемости: result += part не дописывает символы, а строит новую строку из старой и добавки. Для сотни частей это сто промежуточных строк. join() сначала считает итоговую длину, затем заполняет результат один раз.
§7strip(): очистка краёв
Без аргументов strip() удаляет пробельные символы с обоих краёв — все те, что проверяет isspace() из главы 5.8.
raw = "\t\n Python курс \n" print(repr(raw.strip()))
'Python курс'
Пробел между словами остался: метод работает только по краям и внутрь не заглядывает. Это первое, что нужно делать с текстом из input() или из файла.
§8lstrip() и rstrip()
Если чистить нужно только одну сторону, есть отдельные методы.
text = " Python " print(repr(text.lstrip())) print(repr(text.rstrip()))
'Python ' ' Python'
Буквы в названиях — от left и right, та же логика, что у rfind() из главы 5.5.
§9Аргумент — это набор символов
Методу можно передать строку с символами, которые нужно удалять. И вот здесь начинается главное недоразумение темы: аргумент — не подстрока, а набор символов.
text = "!!PyPython!!" print(text.strip("Py!"))
thon
! ! P y P y t h o n ! !
└─────────┘ удаляю: ! ! P y P y — все из набора
└──┘ стоп: "t" в набор не входит
└───┘ с правого края: ! !
Метод движется с каждого края внутрь и удаляет символы, пока они входят в набор. Порядок символов в аргументе не имеет значения: "Py!", "!yP" и "y!P" сработают одинаково.
§10Ловушка strip(".csv")
Самое частое применение этого недоразумения — попытка убрать расширение файла.
print("report.csv".strip(".csv"))
report
Код проходит проверку, попадает в проект и ломается на первом же файле, чьё имя начинается или заканчивается «неудачными» буквами.
print("mycsv.csv".strip(".csv"))
my
Набор ".csv" — это символы ., c, s, v. С левого края у mycsv.csv они не встретились сразу (m не в наборе), зато с правого метод съел .csv, затем v, s, c из самого имени и остановился только на y. Ошибка не проявляется на удобных примерах — и потому доживает до продакшена.
§11removeprefix() и removesuffix()
Для удаления именно подстроки существуют отдельные методы. Они появились в Python 3.9 ровно из-за ловушки выше.
print("mycsv.csv".removesuffix(".csv")) print("unhappy".removeprefix("un"))
mycsv happy
Вызов для "mycsv.csv" | Результат | Что делает |
|---|---|---|
strip(".csv") | "my" | удаляет символы набора с краёв |
removesuffix(".csv") | "mycsv" | удаляет подстроку с конца, один раз |
Если подстроки нет, removesuffix() просто возвращает строку без изменений — ошибки не будет. Правило простое: убираете символы — strip(), убираете слово — removeprefix() или removesuffix().
§12partition(): три части
partition() делит строку по первому вхождению разделителя и возвращает кортеж ровно из трёх строк: до, сам разделитель, после.
record = "language=Python" left, separator, right = record.partition("=") print(repr(left)) print(repr(separator)) print(repr(right))
'language' '=' 'Python'
Это готовая замена приёму с find() из главы 5.5: не нужно ни считать индексы, ни прибавлять единицу. Важно, что делится строка только по первому вхождению.
print("url=http://x?a=1".partition("="))
('url', '=', 'http://x?a=1')Для разбора настроек это именно то, что нужно: ключ отделён, а значение осталось целым.
§13Разделителя нет
Даже когда разделитель не найден, результат остаётся тройкой — меняется лишь её наполнение.
print("Python".partition(":"))
('Python', '', '')Постоянная длина результата — главное достоинство метода: распаковка в три переменные не сломается никогда. Проверять наличие разделителя удобно по средней части — если она пуста, разделителя не было.
§14rpartition()
Как и у find(), есть версия, ищущая с конца.
path = "archive/report/final.txt" print(path.partition("/")) print(path.rpartition("/"))
('archive', '/', 'report/final.txt')
('archive/report', '/', 'final.txt')Для пути к файлу нужен именно rpartition(): он отделяет имя файла от каталогов.
§15partition() или split()
Разделять строку умеет и split() из главы 5.10. Выбирают по тому, сколько частей ожидается.
partition(sep) | split(sep) | |
|---|---|---|
| сколько частей | всегда 3 | сколько получится |
| разделитель в результате | да, отдельной частью | нет, отбрасывается |
| если не найден | тройка с двумя пустыми | список из одного элемента |
| тип результата | кортеж | список |
Правило: делите надвое по первому разделителю — partition(), потому что распаковка предсказуема. Делите на все части — split().
§16Частые ошибки
| Что написано | Что произойдёт | Как правильно |
|---|---|---|
words.join(" ") | AttributeError: у списка нет такого метода | " ".join(words) |
", ".join([1, 2]) | TypeError: элементы не строки | ", ".join(str(n) for n in numbers) |
text.strip(".csv") | удалит символы набора: mycsv.csv станет my | text.removesuffix(".csv") |
| ждать очистки внутри строки | strip() работает только по краям | replace() из главы 5.10 |
text.strip() без сохранения | результат выброшен: строки неизменяемы | text = text.strip() |
a, b = text.partition("=") | ValueError: частей всегда три | принимать три значения |
text.partition("") | ValueError: пустой разделитель | указать непустую строку |
partition() там, где частей много | всё после первого разделителя останется одной строкой | split() |
склейка строк += в длинном цикле | на каждом шаге создаётся новая строка | собрать список и вызвать join() |
§17Проверьте себя
Почему join() вызывается у разделителя, а не у списка?
Это метод строки, и он умеет соединять любую коллекцию — список, кортеж, множество. Иначе его пришлось бы определять для каждого типа отдельно.
Удалит ли strip("ab") подстроку "ab"?
Нет. Аргумент — набор символов: с краёв удаляются любые идущие подряд a и b в любом порядке.
Что вернёт "mycsv.csv".strip(".csv")?
"my". С правого края съедены .csv, а затем v, s, c из самого имени. Нужен removesuffix().
Сколько элементов возвращает partition()?
Всегда три, даже если разделитель не найден — тогда две части пусты.
Как отделить имя файла от пути?
rpartition("/"): он делит по последнему разделителю.
Чем partition() лучше split() для разбора ключ=значение?
Результат всегда из трёх частей, поэтому распаковка не сломается, а знак = внутри значения не разорвёт строку лишний раз.
§18Шпаргалка
", ".join(parts)",".join(str(n) for n in nums)text.strip()text.lstrip()text.rstrip()text.removesuffix(".csv")text.partition("=")path.rpartition("/")Итог главы
join() вызывается у разделителя и требует, чтобы все части были строками, — так он умеет собирать любую коллекцию и делает это за один проход, в отличие от склейки в цикле. strip() удаляет с краёв набор символов, а не подстроку: для удаления слова есть removeprefix() и removesuffix(). partition() делит по первому вхождению и всегда возвращает три части, поэтому распаковка его результата безопасна.