1. Инкапсуляция и приватные атрибуты
Инкапсуляция — сокрытие внутреннего устройства объекта от внешнего кода. В Python приватность — соглашение, подкреплённое манглированием имён: атрибут self.__code превращается в self._Door__code.
class Door:
def __init__(self, code: str) -> None:
self.__code = code # приватный атрибут
def unlock(self, code: str) -> bool:
if self.__is_valid_code(code):
print("Access granted.")
return True
print("Access denied.")
return False
def __is_valid_code(self, code: str) -> bool:
return code == self.__code
Почему именно так: код доступа — внутреннее состояние замка. Прямое изменение door.__code извне ломает логику безопасности. Приватный атрибут сигнализирует: «сюда не лезь».
2. DRY: не дублируй проверку
Методы unlock и change_code оба проверяют код. Вынесем проверку в приватный метод __is_valid_code.
def __is_valid_code(self, code: str) -> bool:
return code == self.__code
Если правила проверки изменятся (например, добавится хеширование), правку придётся сделать в одном месте.
3. Блокировка через datetime
Для временной блокировки нужно запомнить момент, до которого дверь недоступна. Текущее время — datetime.now(), сдвиг — timedelta(minutes=...).
from datetime import datetime, timedelta
class Door:
def __init__(self, code: str, max_attempts: int = 3, block_minutes: float = 15.0) -> None:
self.__code = code
self.__failed_attempts = 0
self.__max_attempts = max_attempts
self.__block_duration = timedelta(minutes=block_minutes)
self.__block_until = None
Проверка «заблокирована ли дверь» — это сравнение datetime.now() < self.__block_until.
4. Пользовательские исключения
Когда встроенных ошибок недостаточно, создаём свой класс-исключение. В практикуме это DoorBlockedError.
class DoorBlockedError(Exception):
'''Raised when trying to access a blocked door.'''
pass
try:
door.unlock("1234")
except DoorBlockedError as e:
print("Дверь заблокирована:", e)
Наследование от Exception позволяет отлавливать ошибку отдельно от других.
5. Современные практики
Решения в этом уроке дополнены аннотациями типов, f-strings и guard clauses — проверками, которые возвращают управление раньше, уменьшая вложенность.
def unlock(self, code: str) -> bool:
if self.__is_blocked():
self.__show_block_time()
return False
if self.__is_valid_code(code):
print("Access granted.")
self.__reset_attempts()
return True
print("Access denied.")
self.__handle_failed_attempt()
return False
Сначала обрабатываем исключительную ситуацию (блокировка), потом — штатный сценарий. Так код читается сверху вниз без лишних else.