Модуль 5 · Строки

Справочник по Python · модуль 5 · строки

Глава 5.9

5.9

Объединение, очистка
и разделение строки

join() собирает строку из частей, strip() удаляет символы по краям, а partition() делит строку ровно в одном месте и всегда возвращает три части. У каждого метода своя граница работы — и своя ловушка.

Категория
Строковые методы
Уровень
Базовый → средний
Связанные темы
Списки, кортежи, распаковка
Зачем это знать
Собирать текст и разбирать записи с разделителем

§1Три границы

Три метода этой главы работают в трёх разных местах строки, и это единственное, что нужно запомнить о них сразу.

Схемагде именно работает каждый метод
join()        между частями:   "a" + сеп + "b" + сеп + "c"
strip()       по краям:        ␣␣␣текст␣␣␣
partition()   в одной точке:   до | разделитель | после

Все три возвращают новый результат и не меняют исходную строку — строки неизменяемы.

§2join(): сборка строки

join() собирает одну строку из набора частей, вставляя разделитель между ними.

main.pyсписок слов превращается в предложение
words = ["изучаем", "методы", "строк"]
sentence = " ".join(words)

print(sentence)
Выводпробел появился только между словами
изучаем методы строк

Разделитель вставляется между элементами, а не после каждого: для трёх слов его копий будет две. По краям строки его нет.

§3Почему метод у разделителя

Запись " ".join(words) поначалу выглядит вывернутой наизнанку: логичнее ждать words.join(" "). Причина в том, что join() — метод строки, а соединять он умеет любую коллекцию.

main.pyодин разделитель, разные коллекции
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() сам не преобразует.

main.pyв списке числа
print(", ".join([1, 2]))
Ошибкауказан индекс проблемного элемента
TypeError: sequence item 0: expected str instance, int found

Числа переводят в строки заранее — включением из главы 4.10 или функцией map() из главы 6.8.

main.pyпреобразование перед сборкой
numbers = [10, 20, 30]

print(", ".join(str(number) for number in numbers))
Выводготовая строка
10, 20, 30

§5Пустой разделитель

Разделитель может быть пустым — тогда части просто склеиваются.

main.pyиз символов обратно в слово
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.

main.pyпробелы, табуляция и перевод строки
raw = "\t\n Python курс \n"
print(repr(raw.strip()))
Выводкрая очищены, середина цела
'Python курс'

Пробел между словами остался: метод работает только по краям и внутрь не заглядывает. Это первое, что нужно делать с текстом из input() или из файла.

§8lstrip() и rstrip()

Если чистить нужно только одну сторону, есть отдельные методы.

main.pyлевая и правая очистка
text = "  Python  "

print(repr(text.lstrip()))
print(repr(text.rstrip()))
Выводобрезано с одной стороны
'Python  '
'  Python'

Буквы в названиях — от left и right, та же логика, что у rfind() из главы 5.5.

§9Аргумент — это набор символов

Методу можно передать строку с символами, которые нужно удалять. И вот здесь начинается главное недоразумение темы: аргумент — не подстрока, а набор символов.

main.pyудаляются P, y и ! в любом порядке
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")

Самое частое применение этого недоразумения — попытка убрать расширение файла.

main.pyкажется, что работает
print("report.csv".strip(".csv"))
Выводрезультат верный — но случайно
report

Код проходит проверку, попадает в проект и ломается на первом же файле, чьё имя начинается или заканчивается «неудачными» буквами.

main.pyто же самое с другим именем
print("mycsv.csv".strip(".csv"))
Выводот имени осталось две буквы
my
Почему это опасно

Набор ".csv" — это символы ., c, s, v. С левого края у mycsv.csv они не встретились сразу (m не в наборе), зато с правого метод съел .csv, затем v, s, c из самого имени и остановился только на y. Ошибка не проявляется на удобных примерах — и потому доживает до продакшена.

§11removeprefix() и removesuffix()

Для удаления именно подстроки существуют отдельные методы. Они появились в Python 3.9 ровно из-за ловушки выше.

main.pyудаляется цельная последовательность
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() делит строку по первому вхождению разделителя и возвращает кортеж ровно из трёх строк: до, сам разделитель, после.

main.pyразбор пары «ключ и значение»
record = "language=Python"
left, separator, right = record.partition("=")

print(repr(left))
print(repr(separator))
print(repr(right))
Выводразделитель сохранён отдельной частью
'language'
'='
'Python'

Это готовая замена приёму с find() из главы 5.5: не нужно ни считать индексы, ни прибавлять единицу. Важно, что делится строка только по первому вхождению.

main.pyзнаков равенства несколько
print("url=http://x?a=1".partition("="))
Выводвторой знак остался в значении
('url', '=', 'http://x?a=1')

Для разбора настроек это именно то, что нужно: ключ отделён, а значение осталось целым.

§13Разделителя нет

Даже когда разделитель не найден, результат остаётся тройкой — меняется лишь её наполнение.

main.pyдвоеточия в строке нет
print("Python".partition(":"))
Выводдве части пусты
('Python', '', '')

Постоянная длина результата — главное достоинство метода: распаковка в три переменные не сломается никогда. Проверять наличие разделителя удобно по средней части — если она пуста, разделителя не было.

§14rpartition()

Как и у find(), есть версия, ищущая с конца.

main.pyпервый и последний разделитель
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 станет mytext.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() делит по первому вхождению и всегда возвращает три части, поэтому распаковка его результата безопасна.

Глава 5.9 · Объединение, очистка и разделение · Справочник по Python Автор: Макаров Максим Николаевич