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

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

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

Регистр: lower, upper, title

Один и тот же текст бывает написан как «Аня», «АНЯ» и «аня» — для Python это три разные строки. Знакомимся с методами регистра: lower, upper, title, capitalize и swapcase.

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

Пользователь не заботится о регистре: в поле имени он напишет «аня», «Аня» или «АНЯ» — и все три варианта для Python различны, ведь сравнение строк идёт посимвольно. Методы регистра приводят текст к единому виду. Они есть у любой строки и вызываются точкой, как всё, что вы видели в уроке про формат-спецификации.

Сценарий: поиск по каталогу

В каталоге на 40 тысяч позиций пользователь пишет в поиск «чайник», а товар лежит под названием «Чайник электрический». Если сравнивать строки как есть, поиск не найдёт ничего, хотя человек написал правильное слово. Единственное препятствие — регистр. Снимают его в одну строку, но с важной оговоркой: нормализуют запрос, а показывают исходное название.

поиск без учёта регистра
catalog = ['Чайник электрический', 'Кофеварка', 'ЧАЙНИК стальной']
query = 'чайник'
found = []
for name in catalog:
    if query in name.lower():
        found.append(name)
print(len(found))
for name in found:
    print(name)
Вывод
2
Чайник электрический
ЧАЙНИК стальной

Два товара нашлись, третий — нет. Важно: в выводе остались и «Чайник», и «ЧАЙНИК» — исходные названия. Нормализация применялась только к запросу, который сравнивался с name.lower(). Если бы к нижнему регистру приводили и само название товара, в каталоге получилось бы два одинаковых ключа и один из товаров молча потерялся бы.

lower и upper: всё вниз и всё вверх

нижний и верхний регистр
word = 'Питон'
print(word.lower())
print(word.upper())
Вывод
питон
ПИТОН

lower сгоняет все буквы в нижний регистр, upper — в верхний. Всё остальное — цифры, пробелы, знаки препинания — остаётся как было: у них регистра нет. Русские буквы обрабатываются так же, как латинские.

title, capitalize и swapcase

три способа навести красоту
phrase = 'коТ Код и тОкен'
print(phrase.title())
print(phrase.capitalize())
print(phrase.swapcase())
Вывод
Кот Код И Токен
Кот код и токен
КОт кОД И ТоКЕН

Три метода решают три разные задачи. title делает заглавной первую букву каждого слова — так оформляют имена и заголовки. capitalize поднимает только первый символ всей строки, а остальные буквы опускает. swapcase меняет регистр каждой буквы на противоположный — редкий гость в рабочем коде, но полезный для понимания: метод работает с каждым символом по отдельности.

Сравнение без учёта регистра

Зачем всё это нужно? Затем, что сравнивать текст людей нужно без учёта регистра. Строки «PYTHON» и «python» не равны, но их lower-версии равны всегда:

сравнение через lower
saved = 'python'
answer = 'PYTHON'
print(answer == saved)
print(answer.lower() == saved.lower())
Вывод
False
True

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

команды в любом регистре
commands = ['СТОП', 'Start', 'выход']
for c in commands:
    print(c.lower())
Вывод
стоп
start
выход

lower против casefold: когда нужен второй

У большинства букв разница только в паде редких случаев. Немецкая ß при полном приведении к регистру превращается в ss, а обычный lower оставляет ß как ß. Для немецкого текста это две разные строки, и сравнение не сойдётся. Python 3.3 добавил отдельный метод casefold() — он приводит строку к виду, пригодному для сравнения, независимо от языка.

немецкая ß в двух методах
print('straße'.lower())
print('straße'.casefold())
print('STRASSE'.casefold())
print('straße'.casefold() == 'STRASSE'.casefold())
Вывод
straße
strasse
strasse
True

Для русского текста lower и casefold ведут себя одинаково, поэтому в большинстве проектов хватает lower. casefold ставят там, где текст сравнивают на разных языках: словари поисковых индексов, коды товаров, импорт из внешних источников.

Регистр и словари

Словарь чувствителен к регистру так же, как и сравнение: «Чай» и «чай» — два разных ключа. Если это не нужно, ключи нормализуют при записи, и товар с другим регистром просто перезапишет прежний.

ключи приведены к одному регистру
prices = {'Чай': 320, 'чай': 340}
print(len(prices))

clean = {}
for name, price in prices.items():
    clean[name.lower()] = price
print(len(clean), clean['чай'])
Вывод
2
1 340

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

МетодЧто делаетПример
lower()всё в нижний регистр'Питон' → 'питон'
upper()всё в верхний регистр'Питон' → 'ПИТОН'
title()заглавная первая буква каждого слова'кот код' → 'Кот Код'
capitalize()заглавная только первая буква строки'коТ Код' → 'Кот код'
swapcase()меняет регистр каждой буквы наоборот'КОт' → 'коТ'
casefold()полное приведение для сравнения'straße' → 'strasse'
результат нужно присвоить
name = 'аня'
name.upper()
print(name)

name = name.upper()
print(name)
Вывод
аня
АНЯ

Первая попытка вычислила 'АНЯ' и тут же выбросила её: строка, которую никто не сохранил, исчезает. Строки неизменяемы — это правило из урока 1, и методы регистра его честно соблюдают: они не правят старую строку, а дарят новую.

Где ещё пригождается приведение регистра

  • Поиск и фильтры: запрос приводят к нижнему регистру, чтобы «Чайник» и «чайник» нашлись вместе.
  • Ключи словарей и теги: приводят при записи, чтобы не плодить дубликаты.
  • Имена файлов: на Windows и macOS регистр не учитывается, на Linux учитывается. Код, который должен работать везде, сравнивает имена приведёнными к одному регистру.
  • Артикулы и идентификаторы: «AB-12» и «ab-12» при импорте схлопываются в один код.
  • Пароли — исключение из правил: их к нижнему регистру не приводят.

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

тег превращается в slug
tag = '  Python для всех  '
clean = tag.strip().lower()
slug = clean.replace(' ', '-')
print(clean)
print(slug)
Вывод
python для всех
python-для-всех

Три операции подряд — и на выходе идентификатор, пригодный для адреса страницы или тега. Заметьте, что replace здесь работает уже по очищенной строке: сначала края, потом регистр, потом разделители. Порядок в таких цепочках решает всё — с пробелами по краям получился бы slug с дефисом в начале.

Мини-практика: коды товаров

Коды приходят из разных источников и отличаются регистром: «AB-12», «ab-12», «Ab-12» — для человека это один и тот же код, а для словаря — три. Приведём кода к общему виду и посчитаем, сколько уникальных на самом деле.

счёт уникальных кодов
codes = ['AB-12', 'ab-12', 'Ab-12', 'CD-34']
seen = {}
for code in codes:
    seen[code.lower()] = seen.get(code.lower(), 0) + 1
print(len(seen))
print(seen)
Вывод
2
{'ab-12': 3, 'cd-34': 1}

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

Как сравнить две строки без учёта регистра

Это отдельный вопрос, и он возникает чаще, чем кажется. Сравнение answer == saved для 'ДА' и 'да' даёт False — и код, который проверяет команду бота или код промо, молча уходит в ветку «не совпало». Приём с .lower() на обеих сторонах вы изучили, но есть три вещи, которые на нём ломаются: когда приводить не нужно, когда приводить опасно и что делать, если в тексте смешаны языки.

парное сравнение без регистра
saved = ['Питон', 'JAVA', 'Go']
incoming = ['питон', 'java', 'GO']
pairs = []
for a, b in zip(saved, incoming):
    pairs.append(a.lower() == b.lower())
print(pairs)
print(all(pairs))
Вывод
[True, True, True]
True

Сравнивать по одному удобно, когда пары должны совпасть позициями. Но чаще нужно «совпало ли хоть одно» — и тут any() вместо all() сэкономит одну строку. Честнее сказать: zip здесь бросит ValueError, если длины списков разойдутся, а в реальном API списки приходят разной длины постоянно. В рабочем коде я бы сравнивал множества: set(x.lower() for x in saved) & set(y.lower() for y in incoming). Множества не упорядочены, но для вопроса «есть ли пересечение» это ровно то, что нужно.

  • Приводить к регистру нужно обе стороны: a.lower() == b.lower(), а не только одну — иначе сравнение останется чувствительным.
  • Не приводите то, что чувствительно к регистру: пароли, токены, контрольные суммы, артикулы. Там регистр — часть значения.
  • Не приводите то, что человек должен увидеть в исходном виде: имя в письме, город в отчёте, название товара в выдаче.

Сортировка и поиск: регистр решает всё

Отдельная боль — сортировка. Python сортирует строки по кодам символов, а не по алфавиту и не по-человечески. Заглавные буквы имеют коды меньше строчных, поэтому 'Апельсин' встанет раньше 'яблоко' — и список выглядит перемешанным. Для поиска по каталогу или списку имён это выглядит как баг.

сортировка без учёта регистра
words = ['яблоко', 'Апельсин', 'банан']
print(sorted(words, key=str.lower))
Вывод
['Апельсин', 'банан', 'яблоко']

Ключ key=str.lower говорит сортировке: сравнивай не сами строки, а их нижнерегистровые копии, а выводи originals. Обратите внимание — в выводе осталась исходная 'Апельсин' с большой буквы. Это то поведение, которое нужно в интерфейсе: порядок по алфавиту, написание как в исходнике. Отдельно скажу про casefold из урока 5 — для русского текста он не даёт ничего, а для немецкой ß меняет всё, поэтому в key= для русских данных берите str.lower.

ЗадачаЧто писатьПочему
Сравнить два вводаa.lower() == b.lower()Обе стороны в одном регистре
Найти в спискеnext((x for x in items if x.lower() == q.lower()), None)Сравнение по ключу, без лишнего списка
Отсортировать по-человеческиsorted(items, key=str.lower)Исходное написание сохраняется
Проверить «это то же слово?»a.casefold() == b.casefold()casefold — для многоязычных сравнений

Про next(...) стоит сказать отдельно: это не магия, а способ получить первый подходящий элемент и None, если ничего не нашлось. Альтернатива — цикл с break, но в генераторном виде он читается как «найди мне первое совпадение», и его удобно передавать в any() или all(): any(x.lower() == q.lower() for x in items).

Практика: приводим импорт к единому виду

Кейс, который встречается в каждом втором проекте — выгрузка из внешней системы. Коды приходят разнобоем: 'AB-12', 'ab-12', 'Ab-12'. Для человека это один артикул, для словаря — три. Наивное решение «пройтись по ключам и привести к нижнему регистру» ломает исходный словарь, и это редко замечают: код работает, просто часть товаров исчезает из выдачи.

счётчик по нормализованному ключу
codes = ['AB-12', 'ab-12', 'Ab-12']
uniq = {}
for code in codes:
    key = code.lower()
    uniq[key] = uniq.get(key, 0) + 1
print(uniq)
Вывод
{'ab-12': 3}

Словарь-счётчик решает обе задачи сразу: показывает, сколько записей схлопнулось (3), и даёт нормализованный ключ для последующего использования. Оригиналы при этом теряются — если нужно и нормализованное имя, и исходное, храните пару: records[key] = original. Мой совет: нормализуйте ключ в момент записи в хранилище, а не при каждом чтении. Тогда все потребители работают с одним и тем же видом данных и никто не вспоминает про .lower() в пятом месте.

Любой метод регистра возвращает новую строку: старая остаётся такой, какой была.

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

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

print('ПиТоН'.lower())
phrase = 'кот код'
print(phrase.title())
print(phrase.capitalize())
answer = 'ДА'
print(answer.lower() == 'да')
Проверь себя
0 / 8

1. Что вернёт 'Питон'.upper()?

2. Чем capitalize отличается от title?

3. Как правильно сравнить ввод пользователя с сохранённым паролем без учёта регистра?

4. Что выведет код: name = 'аня', затем name.upper(), затем print(name)?

5. Что сделает swapcase со строкой 'КОт'?

6. Что произойдёт с цифрами и пробелами при вызове 'кот 2026'.upper()?

7. Что вернёт 'straße'.casefold()?

8. Почему title() опасен для имён и артикулов?

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

Дана строка email с лишними пробелами по краям и заглавными буквами. Приведи её в порядок: убери пробелы по краям методом strip() и приведи всё к нижнему регистру — результат запиши в переменную и выведи.

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

Как привести строку к нижнему регистру в Python?

Методом lower: 'Питон'.lower() вернёт 'питон'. Метод не меняет исходную строку, а возвращает новую — результат нужно сохранить: s = s.lower(). Для верхнего регистра есть парный метод upper.

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

Как сравнить две строки без учёта регистра?

Привести обе к одному регистру и сравнить: answer.lower() == saved.lower(). Это стандартный приём для почт, команд бота и любых данных, которые люди набирают руками, — они почти никогда не бывают в едином регистре.

Чем title отличается от capitalize?

title делает заглавной первую букву каждого слова ('кот код' — 'Кот Код'), capitalize — только первый символ всей строки, опуская остальные ('коТ Код' — 'Кот код'). Для имён и заголовков берут title, для предложений — capitalize.

Почему name.upper() не меняет строку name?

Строки в Python неизменяемы: методы регистра не правят исходник, а возвращают новую строку. Если результат не присвоить — name = name.upper() — он просто исчезнет. То же верно для replace, strip и всех остальных методов строк.

Урок 7: replace

Чем lower отличается от casefold?

lower приводит буквы к нижнему регистру по правилам языка, casefold даёт полностью приведённую форму, пригодную для сравнения. Разница видна на немецкой ß: lower оставит «straße», а casefold превратит её в «strasse». Для русского текста методы равнозначны.

Урок 14: юникод и кодировки

Как найти товар по названию, не различая регистр?

Привести к одному регистру обе стороны и сравнить: if query.lower() in name.lower(). При этом нормализуют только то, с чем сравнивают, — само название товара остаётся как есть, чтобы в выводе не потерялся исходный вид.

Как сравнить две строки без учёта регистра в Python?

Привести обе стороны к нижнему регистру: a.lower() == b.lower(). Для поиска по списку удобнее генератор: any(x.lower() == q.lower() for x in items). Регистр приводят у обеих сторон — привести только одну бессмысленно. Для текста на разных языках используйте casefold() вместо lower(): он даёт форму, пригодную для сравнения.

Урок 5: методы регистра

Почему после lower() строки всё равно не равны?

Потому что lower() убирает только различия в регистре. Пробелы по краям, неразрывные пробелы, «ё» против «е» и «й» против «и» остаются. Порядок приведения важен: сначала приведите текст к одному виду (strip()), потом к регистру (lower()). Если совпадение нужно по всей строке, используйте casefold() — он честнее lower() для сравнений.

Как отсортировать список строк по алфавиту, а не по кодам?

Передайте ключ сортировки: sorted(words, key=str.lower). Python сравнивает не сами строки, а их нижнерегистровые копии, а выводит исходные — так написание сохраняется. Без key заглавные буквы встанут раньше строчных, потому что их коды меньше.

Можно ли приводить к нижнему регистру пароли?

Нет. Приведение к регистру делает разные пароли одинаковыми и снижает стойкость: 'Passw0rd' и 'passw0rd' станут одним ключом. Пароли сравнивают точно, а длинные — хешируют. Регистр допустимо нормализовать только там, где это не ослабляет защиту: имена пользователей для поиска, теги, артикулы.

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

«Любой метод регистра возвращает новую строку: старая остаётся такой, какой была.»

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

TelegramVK

Что дальше

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

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

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