Проверка пароля состоит из нескольких независимых вопросов: хватает ли символов, есть ли цифра, встретились ли буквы обоих регистров. Если интерфейс должен показать все замечания сразу, программа не останавливается после первой ошибки. Она проверяет каждое правило и возвращает нарушения в предсказуемом порядке. Примеры ниже выполнены на CPython 3.14.5.
Сначала зафиксируйте контракт проверки
Фраза «пароль должен быть надёжным» не задаёт алгоритм. Нужны точные условия и форма результата. Например, учебный валидатор может требовать минимальную длину, хотя бы одну цифру, заглавную и строчную букву. Результатом служит список кодов ошибок; пустой список означает, что все проверки пройдены.
До написания кода ответьте на четыре вопроса:
- Граница длины включительная или строгая?
- Что считать цифрой и буквой нужного регистра?
- Нужно вернуть первую ошибку или все ошибки?
- В каком порядке должны идти коды нарушений?
Последние два пункта влияют на структуру программы. Цепочка if — elif выбирает одну ветку, поэтому подходит для взаимоисключающих случаев. Требования к паролю независимы: одна строка может одновременно оказаться короткой, не содержать цифр и не иметь заглавных букв. Для такого контракта каждое правило проверяют отдельно.
len() считает символы строки
Встроенная функция len() возвращает число элементов последовательности. Для строки это число кодовых точек Unicode, а не размер в байтах.
samples = ["code", "пароль", "🔐code"]
for sample in samples:
print(repr(sample), len(sample))
'code' 4
'пароль' 6
'🔐code' 5
На границе легко допустить ошибку на единицу. Если минимальная длина равна восьми, строка из восьми символов проходит это правило, а из семи — нет. В тестах нужны оба случая: только так проверяется сам знак сравнения.
Есть и тонкость Unicode. Визуально одинаковые символы могут состоять из разного числа кодовых точек. Например, é бывает одним символом или сочетанием e и отдельного ударения:
composed = "é"
combined = "e\u0301"
print(len(composed), len(combined), composed == combined)
1 2 False
Для учебной задачи достаточно поведения len(), если контракт не говорит об ином. В продуктовой форме политику Unicode и нормализации задают отдельно: незаметно менять пароль перед сохранением нельзя.
Методы строки проверяют один символ без таблиц алфавита
Методы str.isdigit(), str.isupper() и str.islower() работают и с Unicode. Поэтому ручные диапазоны вроде '0' <= char <= '9' и 'A' <= char <= 'Z' задают более узкую политику, чем строковые методы Python.
characters = ["7", "٧", "²", "A", "Я", "a", "я", "-"]
for char in characters:
print(char, char.isdigit(), char.isupper(), char.islower())
7 True False False
٧ True False False
² True False False
A False True False
Я False True False
a False False True
я False False True
- False False False
isdigit() считает цифрами не только ASCII-символы 0–9: арабско-индийская цифра ٧ и надстрочная ² тоже дают True. Если системе разрешены только ASCII-цифры, это должно быть отдельным требованием. Подмена одного правила другим без явного решения меняет контракт.
У isupper() и islower() похожая семантика. Они учитывают регистр букв разных алфавитов, а пробелы, знаки пунктуации и цифры сами по себе не считают ни верхним, ни нижним регистром.
any() отвечает на вопрос «нашёлся хотя бы один»
any() принимает последовательность логических значений и возвращает True, как только встречает первое истинное. Генераторное выражение позволяет проверить символы без промежуточного списка:
text = "Course 7"
has_space = any(char.isspace() for char in text)
print(has_space)
True
Тот же шаблон подходит для любого строкового предиката: меняется проверка одного char, а обход строки остаётся прежним. Если синтаксис выражения внутри any() пока непривычен, сначала разберите списковые включения и генераторные выражения.
Пустой ввод заслуживает отдельного теста. Для пустого генератора any() возвращает False, потому что в нём нет ни одного истинного элемента. Это естественно для вопросов «есть ли хотя бы одна цифра» и «есть ли хотя бы одна заглавная буква».
Все нарушения собирают независимые проверки
Ранний return после первого нарушения уместен, когда функция отвечает только True или False. Для списка подсказок он мешает: пользователь исправит одну ошибку, отправит форму и лишь тогда увидит следующую.
Рабочая последовательность выглядит так:
- Создать пустой список проблем.
- Выполнить правила независимо друг от друга.
- Добавлять код каждого нарушенного правила в оговорённом порядке.
- Вернуть список после всех проверок.
Это описание намеренно не содержит готовую реализацию задачи. Оно фиксирует структуру, а условия и коды остаются частью практики.
Порядок кодов — часть API. Даже если фронтенд сейчас умеет переставлять сообщения, функция должна соблюдать контракт сама. Иначе одинаковый пароль начнёт выдавать подсказки в разной последовательности после безобидного рефакторинга.
Набор случаев, который ловит типовые ошибки
Одного «хорошего» пароля недостаточно. Минимальная таблица проверок покрывает отношения между правилами:
| Случай | Что он проверяет |
|---|---|
| пустая строка | сразу несколько нарушений и отсутствие особого падения |
| строка на один символ короче границы | строгий знак сравнения длины |
| строка ровно на границе | отсутствие ошибки «на единицу» |
| только строчные буквы | независимые проверки цифры и верхнего регистра |
| одна цифра в начале или конце | обход всей строки, а не одной позиции |
| одна заглавная и одна строчная буква | оба регистра распознаются отдельно |
| несколько нарушений сразу | функция возвращает полный список в нужном порядке |
| не-ASCII цифра или буква | выбранная Unicode-политика работает осознанно |
Функция должна возвращать данные, а не печатать сообщения. print() полезен при отладке, но его вывод вызывающий код не сможет надёжно сопоставить с полями формы. Список коротких кодов отделяет проверку от русского текста интерфейса.
Учебный валидатор не заменяет политику продукта
В реальном сервисе требования к паролю задаёт не случайный набор регулярных выражений, а единая политика регистрации и смены пароля. Клиентская проверка помогает показать подсказку, но сервер обязан повторить проверку: запрос можно отправить без браузерной формы.
Также не стоит незаметно обрезать пробелы или менять регистр. Для логина нормализация может быть частью контракта, а для пароля такое преобразование меняет секрет пользователя. Учебная функция решает более узкую задачу: применяет явно заданные правила к полученной строке.
Практика
В задаче «Валидация пароля на Python» нужно собрать все нарушения и сохранить порядок кодов. Она входит в путь «Python: валидация данных», где тот же подход применяется к другим форматам пользовательского ввода.