Строка в Python неизменяема: её методы не меняют исходный текст. Методы преобразования возвращают строку, но результат поиска может быть числом, проверки — bool, разбиения — списком. Дальше разберём регистр, обрезку, замену и поиск. Код выполнен на CPython 3.14.5.
upper создаёт строку; присваивание сохраняет её
name = ... оно теряется, а исходная строка остаётся прежней.Почему строка не меняется на месте
Присвоить символ по индексу нельзя:
name = "аня"
try:
name[0] = "А"
except TypeError as exc:
print(f"TypeError: {exc}")
TypeError: 'str' object does not support item assignment
Из этого следует главное практическое правило: результат метода надо куда-то положить. Вызов сам по себе ничего не делает.
name = "аня"
name.upper()
print(name)
name = name.upper()
print(name)
аня
АНЯ
Первый вызов вычислил новую строку и выбросил её. Ошибка тихая: программа работает, текст остаётся прежним. У списка наоборот, там append меняет объект на месте, и путаница между двумя поведениями — обычное дело.
Как изменить регистр
Четыре метода, и различаются они тем, где ставят заглавные буквы:
s = "аня иванова"
print(s.upper())
print("АНЯ".lower())
print(s.title())
print(s.capitalize())
АНЯ ИВАНОВА
аня
Аня Иванова
Аня иванова
title() делает заглавной первую букву каждого слова, capitalize() только первую букву строки. Русская «ё» обрабатывается наравне с остальными: "ЁЖ".lower() даёт ёж.
Регистр чаще всего меняют не ради вывода, а ради сравнения:
print("Аня" == "аня", "Аня".lower() == "аня".lower())
False True
Для надёжного сравнения без учёта регистра есть отдельный метод casefold(). Он агрессивнее lower() и раскрывает лигатуры:
print("Straße".lower(), "Straße".casefold())
print("Straße".lower() == "strasse", "Straße".casefold() == "strasse")
straße strasse
False True
Немецкая «ß» после lower() осталась собой, а casefold() превратил её в «ss». Для русского текста разницы нет, но если сравниваешь пользовательский ввод из интернета, бери casefold().
Как убрать лишние пробелы
strip() срезает пробельные символы с обоих концов, включая перевод строки:
raw = " аня@example.com \n"
print(repr(raw.strip()))
print(repr(raw.lstrip()), repr(raw.rstrip()))
'аня@example.com'
'аня@example.com \n' ' аня@example.com'
repr() здесь не для красоты: без него не видно, что именно осталось по краям. Это стандартный приём отладки текста.
А вот со strip("текст") связана ловушка, на которой ошибаются годами. Аргумент — это набор символов, а не подстрока:
print("https://koddo.ru/handbook".strip("https://"))
print("https://koddo.ru/tips".strip("https://"))
koddo.ru/handbook
koddo.ru/ti
Первая строка выглядит правильной, и на ней ошибку обычно не замечают. Вторая показывает правду: у адреса откусило хвост «ps», потому что обе эти буквы входят в набор h, t, p, s, :, /. Метод грызёт символы с краёв, пока они встречаются в наборе, и останавливается на первом чужом.
Убрать именно префикс или суффикс умеют отдельные методы, появившиеся в Python 3.9:
print("https://koddo.ru/tips".removeprefix("https://"))
print("report.csv".removesuffix(".csv"))
print("report.csv".removesuffix(".txt"))
koddo.ru/tips
report
report.csv
Последняя строка показывает разумное поведение: если суффикса нет, строка возвращается без изменений, а не обрезается наугад.
Как заменить часть строки
replace() меняет все вхождения, а третий аргумент ограничивает их число:
text = "заказ 10, заказ 11, заказ 12"
print(text.replace("заказ", "order"))
print(text.replace("заказ", "order", 1))
print("а-б-в".replace("-", ""))
order 10, order 11, order 12
order 10, заказ 11, заказ 12
абв
Заменой на пустую строку выбрасывают символ. Для сложных шаблонов вроде «любая цифра» простого replace не хватит, там нужны регулярные выражения.
Как найти подстроку
Три способа с разным поведением при неудаче:
t = "аня@example.com"
print("@" in t)
print(t.find("@"), t.find("!"))
try:
t.index("!")
except ValueError as exc:
print(f"ValueError: {exc}")
True
3 -1
ValueError: substring not found
in отвечает да или нет и читается лучше всего. find() возвращает позицию или -1. index() делает то же самое, но падает, если не нашёл.
Ловушка find() в том, что -1 — валидный индекс в Python, и проверка if t.find("@"): пропустит случай, когда символ стоит на нулевой позиции, и не заметит -1. Сравнивать надо явно: if t.find("@") != -1:.
Начало и конец проверяют отдельными методами, причём они принимают кортеж вариантов:
f = "report.csv"
print(f.endswith(".csv"), f.startswith("rep"))
print(f.endswith((".csv", ".xlsx")))
True True
True
Считает вхождения count(), и он работает не только с одним символом:
print("абракадабра".count("а"), "абракадабра".count("бра"))
5 2
Как проверить, что в строке
Методы isdigit, isalpha и isalnum отвечают на вопрос о содержимом. Все три дают False для пустой строки. Это не правило для всех методов на is: например, "".isascii() вернёт True.
for v in ["42", "4.2", "", "42 ", "abc"]:
print(repr(v), v.isdigit(), v.isalpha(), v.isalnum())
'42' True False True
'4.2' False False False
'' False False False
'42 ' False False False
'abc' False True True
Строка "4.2" не проходит isdigit(): точка не цифра. Пробел в конце тоже ломает проверку, поэтому перед ней почти всегда идёт strip().
Отдельная тонкость для валидации ввода: у «цифры» три разных метода, и они расходятся на надстрочных знаках и дробях.
for v in ["42", "²", "½"]:
print(repr(v), v.isdecimal(), v.isdigit(), v.isnumeric())
'42' True True True
'²' False True True
'½' False False True
isdecimal() проверяет непустую строку из десятичных цифр без знака. isdigit() пропустит «²», и последующий int() упадёт. Но int() принимает также знак, пробелы по краям и подчёркивания между цифрами: "-42" и "1_000" — допустимый ввод, хотя isdecimal() вернёт False. Для общего случая пробуй int() в try.
Как выровнять и дополнить строку
Три метода добавляют символы до нужной ширины:
print("|" + "имя".ljust(10) + "|")
print("|" + "имя".rjust(10) + "|")
print("|" + "имя".center(10, ".") + "|")
|имя |
| имя|
|...имя....|
Для номеров есть zfill(), и он умнее простого дополнения нулями: знак минуса остаётся впереди.
print("7".zfill(3), "-7".zfill(3))
007 -07
В f-строке то же самое пишется форматом, и когда значение всё равно вставляется в текст, f-строка короче.
Ошибки, на которых спотыкаются
Забыть присвоить результат. text.strip() сам по себе ничего не чинит. Строка неизменяема, метод возвращает новую.
strip() вместо removeprefix(). Набор символов вместо подстроки, и результат выглядит верным ровно до первого неудачного входа.
Цепочка из десяти replace. Когда замен становится много и они зависят от контекста, дешевле разобрать строку на список и собрать заново, чем городить цепочку.
Склейка строк в цикле через +=. Каждая операция создаёт новую строку, потому что старую изменить нельзя. Для сборки из многих кусков берут "".join(список).
isdigit() для проверки числа. Пропускает надстрочные знаки. isdecimal() подходит для записи из десятичных цифр без знака; общий ввод проверяют через try и int() или float(), как в статье про исключения.
Частые вопросы
Как убрать пробелы внутри строки, а не по краям
replace(" ", "") убирает все пробелы, " ".join(text.split()) схлопывает подряд идущие в один. strip() работает только с краями.
Как проверить, что строка пустая
Проверкой if not text:, потому что пустая строка ложна сама по себе. Если строка может содержать одни пробелы, сначала strip(): if not text.strip():.
Как посчитать символы в строке
len(text). Метод count() считает вхождения конкретной подстроки, а не длину.
Чем строка отличается от списка
Строка неизменяема, список нет. text.upper() возвращает результат, не меняя text, а items.append(x) меняет список на месте. Но не каждый метод списка его меняет: index() и count() только возвращают результат.
Где потренироваться
В пути «Python с нуля» на Koddo под текст отведён отдельный пак: нормализация ввода, разбор строки на части, сборка отчёта. Проверить себя на живой задаче можно на проверке пароля, где как раз нужны проверки содержимого и аккуратная работа с границами; если застрянешь — есть полный разбор этой задачи со всеми ошибками, на которых спотыкаются.