Чему научитесь
- Пользоваться готовыми методами вместо самодельных циклов
- Понимать, почему метод не меняет строку, а возвращает новую
- Разбивать текст на слова и собирать обратно
- Отличать случаи, где метод есть, от тех, где нужен цикл
Как устроено занятие
Сначала разбор с примерами и короткими проверками понимания — они нужны, чтобы поймать непонятое сразу, а не через три темы.
Дальше 15 задач лестницей: разминка, основа, со звёздочкой. Занятие засчитывается, когда решено 10 — остальные не пропадают и учитываются отдельно.
После занятия — вторая часть, ещё 15 задач на те же приёмы в новых сюжетах.
Сколько это займёт
Примерно час-полтора вместе с задачами. Сроков нет: можно закрыть вкладку и вернуться когда удобно — прогресс сохранится.
Действия, которые строка умеет сама
В прошлом занятии всё делалось вручную: посчитать символы — цикл, найти позицию — цикл, убрать символ — снова цикл. Это было полезно: теперь понятно, что происходит внутри.
А внутри Python это уже написано. У строки есть методы — действия, которые она выполняет над собой:
s = "Hello World"
print(s.lower()) # hello world
print(s.upper()) # HELLO WORLD
print(s.count("l")) # 3
print(s.find("World")) # 6 — номер, с которого начинается
Записывается через точку: сначала строка, потом имя метода, потом скобки. В скобках — то, что методу нужно знать.
| метод | что делает |
|---|---|
s.lower(), s.upper() |
новая строка в нужном регистре |
s.count(x) |
сколько раз встречается x |
s.find(x) |
номер первого вхождения или −1 |
s.replace(a, b) |
новая строка, где все a заменены на b |
s.strip() |
новая строка без пробелов по краям |
Обратите внимание на слово «новая» — оно ключевое, и следующий блок весь про него.
Строку нельзя изменить
Это главное, что нужно понять про строки в Python, и главный источник ошибок.
Строка неизменяема. Однажды созданная, она остаётся такой навсегда. Ни один метод её не меняет — каждый возвращает новую строку.
s = "hello"
s.upper() # новая строка создана… и потеряна
print(s) # hello — s не изменился
Программа не падает, не ругается, просто ничего не делает. Ошибку такого рода ищут долго именно потому, что код выглядит правильным.
Чтобы результат сохранился, его надо куда-то положить:
s = s.upper() # теперь s — новая строка
print(s) # HELLO
Или сразу использовать: print(s.upper()).
По той же причине не работает s[0] = "x" — изменить символ на месте нельзя. Нужна новая строка: через replace, через сборку в цикле или через методы, которые вы узнаете дальше.
Зато методы легко ставятся в цепочку — каждый получает результат предыдущего:
print(s.lower().strip().replace(" ", "-"))
Проверка: что напечатается
s = "abc"
s.replace("a", "z")
print(s)
Что выведет программа?
split и join: разобрать и собрать
Два метода, ради которых стоит выучить остальные. split разбивает строку на слова:
s = " one two three "
parts = s.split() # ["one", "two", "three"]
print(len(parts)) # 3 — сколько слов
for word in parts:
print(word)
Без аргументов split сам разбирается с несколькими пробелами подряд и с пробелами по краям — вручную это писать заметно дольше.
То, что он возвращает, называется списком. Подробно списки — следующий модуль, а пока достаточно двух свойств: список можно перебрать циклом for и узнать его длину через len.
join делает обратное — собирает слова в строку. Вызывается он у разделителя, и это выглядит непривычно:
print("-".join(parts)) # one-two-three
print(" ".join(parts)) # one two three
print("".join(parts)) # onetwothree
Читается как «склей это через дефис». Пара split + join решает половину задач на текст: разобрал, что-то сделал, собрал обратно.
Проверки: isdigit, isalpha и другие
Вместо сравнения с границами алфавита есть готовые проверки. Они отвечают True или False:
| проверка | истинна, когда |
|---|---|
s.isdigit() |
все символы — цифры |
s.isalpha() |
все символы — буквы |
s.isupper(), s.islower() |
все буквы нужного регистра |
s.startswith(x), s.endswith(x) |
строка начинается или кончается на x |
Работают они и на целой строке, и на отдельном символе — символ ведь тоже строка, просто из одного знака:
for ch in s:
if ch.isdigit():
total += int(ch)
Две ловушки
"-12".isdigit() — это False. Минус не цифра, и точка тоже: "12.5".isdigit() даёт False. Проверка отвечает буквально на вопрос «все ли символы цифры», а не «похоже ли это на число».
И вторая: пробел не буква. У строки "a b" метод isalpha() вернёт False, хотя букв там две.
Проверка: что вернёт isdigit
Для каких из этих строк isdigit() вернёт True?
Когда метод, а когда цикл
Метод короче и работает быстрее написанного вручную цикла. Но подходит он не всегда.
| задача | чем решать |
|---|---|
| посчитать вхождения символа или куска | count |
| найти позицию | find |
| заменить одно на другое | replace |
| разбить на слова, собрать обратно | split, join |
| проверить состав строки | isdigit, isalpha |
| условие сложнее, чем «равно» | цикл |
| нужно сравнивать соседние символы | цикл |
| нужно что-то накопить по ходу | цикл |
Правило простое: есть готовый метод — берите метод. Написанный вручную аналог count не станет ни понятнее, ни быстрее.
Но как только условие перестаёт быть простым сравнением — «символ, у которого сосед слева гласный», — метода не найдётся, и работает обычный цикл из прошлого занятия. Одно не отменяет другого.
Часто удобна смесь: разбить строку методом, а дальше перебрать циклом. Так решается почти всё в сегодняшней практике.
Три ошибки этого занятия
Результат метода потерян. s.replace("a", "b") без присваивания. Признак: программа выводит исходную строку и выглядит при этом безупречно. Проверяйте: у каждого вызова метода результат либо присвоен, либо сразу напечатан.
Путаница между find и «нашлось». find возвращает −1, когда не нашёл, а не 0. И нумерация у него с нуля. Если в условии позиции считаются с единицы, преобразование делается один раз и аккуратно.
isdigit принят за «это число». Для -12 и 12.5 он даёт False. Если в задаче встречаются отрицательные числа или дроби, эта проверка не годится.
Как проверять себя
- строка из одних пробелов —
split()даст пустой список, и цикл по нему не выполнится ни разу; - искомого нет вовсе — что вернут
findиcount, и что должна вывести программа; - строка из одного слова — не рассчитывает ли решение хотя бы на два.
Практика: пятнадцать задач
Лестница прежняя: пять разминочных, семь основных, три со звёздочкой. Зачёт при десяти решённых.
Почти каждую задачу можно решить и вручную, как в прошлом занятии, и методом. Решайте методом — в этом смысл занятия. Ручной способ вы уже показали.
Срезов здесь не будет: они следующее занятие. Поэтому там, где хочется «взять кусок строки», пока обходимся split, replace и перебором.
Методы вызваны, а строка прежняя
Задача: «дана строка; выведите её строчными буквами, без пробелов по краям и с заменой всех дефисов на пробелы».
Ученик написал:
s = input()
s.lower()
s.strip()
s.replace("-", " ")
print(s)
Программа выводит строку в точности такой, какой её ввели. Объясните, почему так происходит, и приведите два способа исправить — с промежуточной переменной и без неё.