Статья будет полезной для начинающих программистов изучающих Python, хотя приведенный код не такой уж и простой.
В этой статье я буду разрабатывать модуль для проверки надежности пароля. Где он может быть использован, пожалуй, излишне перечислять. Все знают, что пароль является надежным тогда, когда он будет содержать числа, буквы верхнего и нижнего регистра, и не будет слишком коротким.
Итак, надежный пароль должен удовлетворять следующим требованиям:
- длина 10 символов и более;
- должен иметь хотя бы одно число;
- должен иметь хотя бы одну букву в верхнем регистре;
- должен иметь хотя бы одну букву в нижнем регистре;
- пароль может содержать только латинские буквы.
Функция для проверки пароля будет принимать строку и возвращать False или True.
1 2 |
def password(data: str) -> bool: pass |
Начало положено! 😊 Двигаемся дальше.
Вариант 1.
Первое, что приходит в голову — использовать модуль re встроенной библиотеки модулей. С этого и начну.
Небольшое предостережение для тех, кто не знаком с этим модулем. Имейте терпение и все равно читайте! Дальше будет пример кода без импорта модулей.
Сначала нужно правильно сформулировать шаблон регулярного выражения. Это часто бывает труднее всего сделать. Для совпадений символов верхнего регистра соответствует регулярное выражение ‘[A-Z]’,нижнего регистра — «[a-z]»,ну и числа —«[0-9]» или«\d».Напомню для тех, кто не помнит. Выражение в квадратных скобках, это квантификатор и означает один символ из диапазона что в скобках, \ d — сокращенная запись [0-9].
Имея регулярные выражения удовлетворяющие требования надежного пароля, написать нашу функцию не составит труда.
1 2 3 4 |
import re def password(data: str) -> bool: L = (Len(data) > = 10 re.search(r'[az]', data), re.search(r'[AZ]', data), re.search(r'\d', data)) return all(L) |
Подробнее о работе функции.
L — кортеж, элементами которого являются:
len(data) > = 10 — выражение: размер входных данных больше или равен 10, логическое значение True или False;
re.search (pattern, string) — метод модуля re, параметрами которого является шаблон регулярного выражения и строка; ищет совпадения в строке с шаблоном регулярного выражения; возвращает объект или ничего.
all(L) — функция all, аргументом которой является кортеж L, вернет True, когда каждый элемент аргумента будет иметь логическое True, иначе — False.
Тестирование.
Для тестирования работы функции необходимо записать входные данные, ожидаемый результат, и еще раз входные данные в кортеж. В цикле распаковываю кортеж, вызываю утверждение assert,которое проверяет условие на истинность. Если условие истинно, код работает дальше, если нет — генерирует ошибку на которую ссылается переменная m.
1 2 3 4 5 6 7 8 9 10 |
if __name__ == '__main__': test = (('A1213pokl', False, 'A1213pokl'), ('bAse730onE4', True, 'bAse730onE4'), ('asasasasasasasaas', False, 'asasasasasasasaas'), ('QWERTYqwerty', False, 'QWERTYqwerty'), ('123456123456', False, '123456123456'), ('QwErTy911poqqqq', True, 'QwErTy911poqqqq')) for s, boo, m in test: assert password(s) == boo, m mprint("Функция все тесты прошла успешно!") |
И так, функция все тесты прошла успешно, можно отдыхать. Отдых — это полезно, но и о функции не забываем! Отдыхаем и думаем: как эту функцию можно еще оптимизировать?
А вот как:
1 2 |
def password(data: str) -> bool: return len(data) > 9 and all(re.search(p, data) for p in ('[AZ]', '\d', "[az]')) |
Опять тесты! Тесты! И еще раз тесты! Тестировать можно и так:Здесь логический оператор and вернет True, если выражения слева и справа от него будут истина, иначе — False. Выражение слева проверит пароль на количество знаков, выражение справа, через знакомую уже функцию all, проверит истинность каждого элемента генератора в круглых скобках.
1 2 3 4 5 6 7 |
if __name__ == '__main__': assert password('werQW123') == False, 'Test 1' assert password('WEcv12347') == False, 'Test 2' assert password('123456789') == False, 'Test 3' assert password('AAAAAAaaa') == False, ' Test 4' assert password('1234ADjkse') == True, 'Test 5' print('Функция все тесты прошла успешно!') |
Осталось только задокументировать функцию. Теперь модуль будет выглядеть так:
1 2 3 4 5 |
def password(data: str) -> bool: '''Функция для проверки паролей на надежность: не менее 10 символов, одна или более букв верхнего регистра, одна или более букв нижнего регистра, одна или более цифр, латинские буквы''' return len(data) > 9 and all(re.search(p, data) for p in ('[A-Z]', '\d', '[a-z]')) |
Вариант 2.
Воспользуюсь простой логикой. В каких случаях функция должна вернуть False?
- Если входные данные содержат только цифры.
- Если входные данные содержат только буквы.
- Если входные данные в верхнем регистре.
- Если входные данные в нижнем регистре.
- Если длина входных данных меньше 10.
Это все можно проверить строчными методами и встроенными функциями Пайтона без импорта модулей стандартной библиотеки.
1 2 3 4 5 6 7 |
def password(data): return not(len(data) < 10 or data.isdigit() or data.isalpha() or data.islower() or data.isupper()) \ and data.isalnum() |
Думаю, излишне напоминать о том, что после каждых изменений в функции, необходимо ее снова проверять подготовленными тестами.
Аналогично еще один вариант:
1 2 3 4 5 6 7 8 |
def password(data): if len(data) < 10: return False if data.upper() == data: return False if data.lower() == data: return False return any(c.isdigit() for c in data) |
Вариант 3.
1 2 3 4 5 6 |
def password(data): foo = (bool(len(data)>9), any(map(lambda x: x.isdigit(), data)), any(map(lambda x: x.islower(), data)), any(map(lambda x: x.isupper(), data))) return all(foo) |
В этом варианте логические значения условий пароля хранятся в кортеже на который ссылается переменная foo. Первый элемент foo содержит логическое значение условия длины пароля, все остальные — логические значения (функция any) объекта map, который в свою очередь содержит логические значения проверки каждого элемента data на принадлежность к цифрам, буквам нижнего или верхнего регистра. Все это делает функция map, через вызов функции lambda для каждого элемента data. Лямбда использует строчные методы проверки: isdigit() — есть ли это цифра, islower() — есть ли это буква в нижнем регистре, isupper()— или буква в верхнем регистре. Если объект map содержит хотя бы одно True, то функция map вернет True, иначе — False. Ну и функция all вернет True, если все элементы foo будут True (будут удовлетворять условиям надежного пароля), иначе — False.
Конечно, вариантов еще можно найти и найти … Можно использовать другие модули и т.д. Но всегда нужно добиваться оптимального варианта.