Перейти к содержанию

412 уроков, 25 библиотек и челлендж «Что выведет код?» — бесплатно, код прямо в браузере

Начать обучение
Урок 7 из 20 Начальный 15 мин 120 XP

replace: замена подстрок

Заменить одно слово на другое во всём тексте, убрать мусор из середины строки, вычистить телефон до одних цифр — всё это делает один метод: replace.

Редакция Питоники

strip чистит края строки, но мусор часто сидит в середине: старое название товара внутри описания, лишние пробелы между словами, дефисы в номере телефона. Метод replace заменяет подстроку где угодно — и делает это со всеми вхождениями сразу.

Сценарий: телефон приходит с мусором

Пользователь вводит телефон как удобно: «+7 (912) 000-00-00», «8 800 555-35-35», «тел.: 8-800-555-35-35». Для человека это один и тот же номер, для программы — три разные строки. Сравнивать их напрямую бессмысленно: сначала нужно привести к общему виду, и вот тут replace незаменим.

телефоны в разном оформлении
phones = ['+7 (912) 000-00-00', '8 (800) 555-35-35']
for p in phones:
    digits = p.replace(' ', '').replace('(', '').replace(')', '').replace('-', '')
    print(digits)
Вывод
+79120000000
88005553535

Четыре замены подряд убрали пробелы, скобки и дефисы, и остались одни цифры с плюсом. Порядок здесь не важен — все четыре замены независимы, — но длина цепочки всегда равна числу разных видов мусора. Если в данных вдруг появится точка или двоеточие, цепочку придётся дополнить, и забыть об этом легко: программа не ругается, а просто пропускает символ.

replace: все вхождения сразу

замена во всей строке
phrase = 'кот, код, кот'
print(phrase.replace('кот', 'пёс'))
Вывод
пёс, код, пёс

Схема: s.replace(старая, новая) — найди каждую подстроку «старая» и поставь на её место «новая». Обратите внимание: заменились оба вхождения, а не только первое — replace по умолчанию обрабатывает строку целиком. Слово «код» не пострадало: replace ищет подстроку целиком, и «код» на «кот» не похож.

подстроки нет — строка возвращается как была
text = 'кот'
print(text.replace('пёс', 'кот'))
Вывод
кот

Если старой подстроки в строке нет, replace не ругается: он вернёт исходную строку без изменений. Это удобное свойство для чистки данных — вызов можно ставить без всяких проверок «а есть ли там что-то».

Третий аргумент: сколько замен сделать

ограничение числа замен
text = 'а-а-а-а'
print(text.replace('-', '.', 2))
print(text.replace('-', '.'))
Вывод
а.а.а-а
а.а.а.а

Необязательный третий аргумент считает замены слева направо и останавливается, когда лимит исчерпан: replace('-', '.', 2) заменил первые два дефиса, третий устоял. Без лимита заменились все. Это же число работает и в обратную сторону: заменить нужно не всё, а только первое вхождение — ставьте лимит 1.

Замена на пустую строку — удаление

подстрока вырезается
messy = 'к о т   к о д'
print(messy.replace(' ', ''))
Вывод
коткод

Грабли: порядок замен и регистр

Две ошибки в цепочках replace встречаются чаще всего. Первая — порядок замен: если новая подстрока содержит старую, вторая замена может испортить результат. Вторая — регистр: replace ищет подстроку буквально, поэтому «Кот» и «кот» для него разные вещи.

порядок замен решает всё
s = 'аб'
print(s.replace('а', 'б').replace('б', 'а'))
print(s.replace('б', 'а').replace('а', 'б'))
Вывод
аа
бб

Обе цепочки меняют местами две буквы, но дают разный результат. В первой первая замена превратила «а» в «б», и вторая переписала уже обе буквы обратно в «а» — получилась «аа». Во второй всё наоборот: «б» стала «а», а потом обе «а» превратились в «б». Правило простое: если новая подстрока содержит старую, сначала избавляются от старой, и только потом делают замену — или подставляют временную метку.

replace не различает регистр
text = 'Кот и кот и КОТ'
print(text.replace('кот', 'пёс'))
Вывод
Кот и пёс и КОТ
буква или слово
price = '1250.50 руб.'
print(repr(price.replace('руб', '')))
print(repr(price.replace('р', '')))
Вывод
'1250.50 .'
'1250.50 уб.'

Цепочки вызовов

replace возвращает новую строку — значит, у неё сразу можно вызвать следующий replace. Так собирают конвейер чистки: каждый шаг вырезает свой мусор.

из сырой строки — к числу
raw = '  Цена: 1 000 руб.  '
clean = raw.strip().replace(' руб.', '').replace('Цена:', '').replace(' ', '')
print(clean)
Вывод
1000

Цепочка сработала слева направо: strip снял края — остатки разбирали три replace подряд. Результат записан в переменную — без присваивания вся работа пропала бы даром: строки неизменяемы, и методы регистра живут по тому же правилу. Финальный пример — исправить опечатку во всём отчёте одной строкой на элемент:

правка по всем строкам отчёта
report = ['ошибка в строке 1', 'ошибка в строке 2']
for line in report:
    print(line.replace('ошибка', 'правка'))
Вывод
правка в строке 1
правка в строке 2
последний дефис — через rfind и срез
s = 'а-б-в-г'
print(s.replace('-', '+', 1))
print(s[:s.rfind('-')] + '+' + s[s.rfind('-') + 1:])
Вывод
а+б-в-г
а-б-в+г

Когда replace уже не справляется

Replace умеет искать только точную подстроку. Как только в данных появляются варианты — «кот», «Кот», «кОт», «котик» — цепочка замен разрастается и всё равно находит не всё. Тогда задача меняется: нужен уже не текстовый поиск, а разбор по шаблону.

Практическое правило: если список замен перевалил за две-три штуки, стоит остановиться и подумать, не проще ли разобрать строку на части и собрать заново. Часто дешевле split плюс обработка каждой части, чем десяток replace подряд.

Чем что чинить

ЗадачаИнструментПример
Убрать пробелы по краямstrip' кот ' → 'кот'
Убрать мусор из серединыreplaces.replace('-', '')
Заменить подстроку на другуюreplaces.replace('кот', 'пёс')
Разобрать на части по разделителюsplitrow.split(',')
Собрать из частей обратноjoin', '.join(parts)
Взять кусок по границамсрезs[4:8]

Replace занимает в этой таблице две строки, и это честно: он единственный из строковых методов, который умеет менять содержимое, не трогая длину строки и не разбирая её на части. Остальные либо чистят края, либо режут.

Замена внутри списков и словарей

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

чистка всех элементов разом
words = ['кот-1', 'код-2']
fixed = [w.replace('-', '_') for w in words]
print(fixed)

prices = {'кот-1': 100}
clean = {k.replace('-', '_'): v for k, v in prices.items()}
print(clean)
Вывод
['кот_1', 'код_2']
{'кот_1': 100}

Списком и словарём — два разных случая, а идея одна: взять старое значение, применить к нему replace и положить результат в новое место. Ключи словаря при этом тоже можно чистить — просто замену пишут для ключа, а не для значения.

Как replace работает

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

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

Мини-практика: чистим список телефонов

Три телефона в разном оформлении. Приведите их к виду, в котором остаются только цифры и плюс, и выведите результат для каждого.

нормализация телефонов
phones = ['+7 (912) 000-00-00', '8-800-555-35-35', 'тел.: 8 800 5553535']
for p in phones:
    clean = p.replace(' ', '').replace('-', '').replace('(', '').replace(')', '').replace('тел.:', '')
    print(clean)
Вывод
+79120000000
88005553535
88005553535

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

Как заменить подстроку в Python: replace

replace — самый прямолинейный метод строк: берёт подстроку, заменяет её на другую и возвращает новую строку. Ничего хитрого, но три вещи стоит знать сразу. Первое: заменяются ВСЕ вхождения, если не сказать иное. Второе: регистр учитывается — replace не догадывается, что Error и error — одно и то же. Третье: метод ничего не находит и не сообщает об этом: если подстроки в строке нет, вы получите ту же строку без ошибки.

замена всех вхождений и только первых
s = 'a-b-c-d'
print(s.replace('-', '+'))
print(s.replace('-', '+', 2))
Вывод
a+b+c+d
a+b+c-d

Третий аргумент — это ограничение по количеству замен, а не по позиции. s.replace('-', '+', 2) заменит первое и второе вхождения по порядку, третье и четвёртое останутся. Если нужно заменить только в начале строки — это уже другая задача, и replace для неё не годится: берите срез или startswith.

Цепочки replace: чистка чисел и дат

Типичная задача — привести к числу строку, в которой человек написал цену с пробелами, символом валюты и -разделителями. Один replace тут не справится, а вот цепочка работает. Порядок вызовов имеет значение: сначала убирают мусор, потом пробелы, и только в конце превращают в число.

цена из строки в число
price = '1 250.50 руб.'
clean = price.replace(' руб.', '').replace(' ', '')
print(clean)
print(float(clean))
Вывод
1250.50
1250.5

Обратите внимание на вывод: float('1250.50') печатается как 1250.5 — это нормальное поведение float, а не ошибка. Если нужны ровно два знака после запятой, форматируйте при выводе: f'{value:.2f}'. И ещё: float() терпит пробелы вокруг числа, но не внутри — '1 250.50' без замены пробела даст ValueError. Это тот случай, когда лучше не гадать, а взять try/except и сообщить пользователю, что в поле цены попал лишний разделитель.

телефон: убираем всё, кроме цифр
phone = '+7 (900) 123-45-67'
digits = phone
for ch in ' ()+-':
    digits = digits.replace(ch, '')
print(digits)
print(len(digits))
Вывод
79001234567
11

Цикл по набору символов — рабочий, но медленный способ: для каждого символа проход по всей строке. На коротких номерах это незаметно, на выгрузке из CRM в миллион строк — уже нет. Быстрее отфильтровать: ''.join(ch for ch in phone if ch.isdigit()). Оно и читается точнее: «оставь только цифры», а не «убери эти пять символов».

Что нужноКак написатьЗамечание
Заменить все вхожденияs.replace('old', 'new')Регистр учитывается
Заменить первые Ns.replace('old', 'new', N)N — количество, не позиция
Убрать мусор из числаfloat(s.replace(' ', '').replace(',', '.'))Порядок замен важен
Оставить только цифры''.join(ch for ch in s if ch.isdigit())Один проход вместо цикла
Заменить без учёта регистраs.lower().replace(old.lower(), new)Меняет и найденное, и новое
Убрать подстроку целикомs.replace('old', '')Это и есть удаление

Про replace и регулярные выражения стоит сказать отдельно, потому что это частый вопрос. replace работает с обычным текстом: если в подстроке есть специальные символы — «(», «+», «*» — они ищутся буквально. Замены по шаблону (d+, [а-я]+) через replace не сделать. Для таких задач есть re.sub — но это уже курс регулярных выражений, и тянуть его в базовый разбор строк я бы не стал.

И последнее, о чём стоит помнить: replace не работает в списках. Он есть у строки, и всё. Если в списке десять элементов и в каждом надо заменить подстроку — придётся либо пройти циклом с пересборкой списка, либо вспомнить про списковое включение из другого раздела. Для коротких списков цикл читается лучше, для длинных — быстрее включение.

Замена на пустую строку — это удаление: Python просто вырезает подстроку и склеивает края.

Что выведет код?

Сначала предскажи ответ в голове — это главный навык программиста.

print('а-а-а'.replace('-', '+'))
s = 'кот кот кот'
print(s.replace('кот', 'пёс', 2))
s = 'коткод'
print(s.replace('код', '', 1))
Проверь себя
0 / 8

1. Что вернёт 'кот кот кот'.replace('кот', 'пёс')?

2. Что означает третий аргумент в s.replace('а', 'б', 2)?

3. Как «удалить» все пробелы из строки s?

4. Что произойдёт после text = 'кот' и вызова text.replace('кот', 'пёс') без присваивания?

5. Что вернёт replace, если искомой подстроки в строке нет?

6. Зачем в цепочке пишут несколько replace подряд?

7. Что вернёт 'аб'.replace('а', 'б').replace('б', 'а')?

8. Почему 'Кот и кот и КОТ'.replace('кот', 'пёс') меняет только одно слово?

Карточки терминов
Запомнено: 0 / 8
Практика

Дана строка phone с телефоном в разном мусоре: пробелы, скобки и дефисы. Собери цепочку replace, которая оставит только номер без мусора, и выведи результат.

practice.py
Вопросы и ответы по уроку

Как заменить подстроку в строке в Python?

Методом replace: s.replace('кот', 'пёс') вернёт новую строку, где все вхождения «кот» заменены на «пёс». Метод не меняет исходную строку — результат нужно присвоить: s = s.replace('кот', 'пёс').

Урок 1: неизменяемость строк

Как заменить только первое вхождение подстроки?

Передать третьим аргументом лимит замен: s.replace('кот', 'пёс', 1). Замены идут слева направо и остановятся после первого совпадения, всё остальное останется как было.

Как удалить символ или подстроку из строки?

Заменить на пустую строку: s.replace(' ', '') уберёт все пробелы, s.replace('-', '', 1) — только первый дефис. Отдельного метода удаления у строк нет — замена на пустую строку и есть удаление.

Чем replace отличается от strip?

strip снимает символы только с краёв строки и по набору символов, replace меняет точную подстроку по всей длине, включая середину. Для чистки пробелов по краям берут strip, для правки текста внутри — replace.

Урок 6: strip

Как убрать из строки все точки и запятые?

Заменой на пустую строку: s.replace('.', '').replace(',', ''). Метод удаления у строк нет, но замена на пустую строку делает ровно то же самое — вырезает вхождения и склеивает края.

Почему после replace ничего не изменилось?

Строки неизменяемы: метод вернул новую строку, но её не сохранили. Правильная запись — s = s.replace('кот', 'пёс'). Второй случай — подстрока не совпала: replace ищет буквально, без учёта регистра и смысла.

Урок 1: неизменяемость строк

Понравился урок? Сошлитесь на него

«Замена на пустую строку — это удаление: Python просто вырезает подстроку и склеивает края.»

Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.

TelegramVK

Что дальше

Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.

Похожие уроки по темам

Подобраны автоматически по пересечению тем и ключевых слов.