Решение задания 1. Класс Door
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 = Door("1234")
door.unlock("0000")
door.unlock("1234")
Логика решения: __code приватный, чтобы внешний код не мог прочитать или изменить код напрямую. Метод unlock возвращает bool: это удобно для дальнейших проверок и тестов.
Решение задания 2. Смена кода
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 change_code(self, old_code: str, new_code: str) -> None:
if not self.__is_valid_code(old_code):
print("Access denied. Code not changed.")
return
self.__code = new_code
print("Code changed.")
def __is_valid_code(self, code: str) -> bool:
return code == self.__code
# Пример использования
door = Door("1234")
door.change_code("0000", "9999")
door.unlock("1234")
door.change_code("1234", "9999")
door.unlock("1234")
door.unlock("9999")
Логика решения: guard clause в change_code сразу обрабатывает неверный старый код. Общая проверка вынесена в __is_valid_code, поэтому правило «код совпадает с __code» находится в одном месте.
Решение задания 3. Блокировка двери
import time
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
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
def change_code(self, old_code: str, new_code: str) -> None:
if self.__is_blocked():
self.__show_block_time()
return
if self.__is_valid_code(old_code):
self.__code = new_code
self.__reset_attempts()
print("Code changed.")
return
print("Access denied. Code not changed.")
self.__handle_failed_attempt()
def __is_valid_code(self, code: str) -> bool:
return code == self.__code
def __is_blocked(self) -> bool:
if self.__block_until is None:
return False
return datetime.now() < self.__block_until
def __show_block_time(self) -> None:
remaining = self.__block_until - datetime.now()
seconds = int(remaining.total_seconds())
minutes, sec = divmod(seconds, 60)
print(f"Door is blocked. Try again in {minutes} min {sec} sec.")
def __check_block(self) -> None:
if self.__failed_attempts >= self.__max_attempts:
self.__block_until = datetime.now() + self.__block_duration
print("Too many failed attempts. ", end="")
self.__show_block_time()
def __handle_failed_attempt(self) -> None:
self.__failed_attempts += 1
self.__check_block()
def __reset_attempts(self) -> None:
self.__failed_attempts = 0
# Пример использования
door = Door("1234", max_attempts=2, block_minutes=0.05)
door.unlock("1111")
door.change_code("2222", "9999")
door.unlock("1234")
time.sleep(5)
door.unlock("1234")
Логика решения:
__failed_attemptsобщий дляunlockиchange_code.- При успехе счётчик сбрасывается, иначе одна старая ошибка приведёт к блокировке при следующем правильном коде.
__check_blockустанавливает__block_untilтолько при достижении лимита.- Пока
datetime.now() < __block_until, любая операция отклоняется с сообщением об оставшемся времени.
Решение задания 4. Исключение при блокировке
import time
from datetime import datetime, timedelta
class DoorBlockedError(Exception):
'''Raised when trying to access a blocked door.'''
pass
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
def unlock(self, code: str) -> bool:
if self.__is_blocked():
raise DoorBlockedError(self.__remaining_block_time())
if self.__is_valid_code(code):
print("Access granted.")
self.__reset_attempts()
return True
print("Access denied.")
self.__handle_failed_attempt()
return False
def change_code(self, old_code: str, new_code: str) -> None:
if self.__is_blocked():
raise DoorBlockedError(self.__remaining_block_time())
if self.__is_valid_code(old_code):
self.__code = new_code
self.__reset_attempts()
print("Code changed.")
return
print("Access denied. Code not changed.")
self.__handle_failed_attempt()
def __is_valid_code(self, code: str) -> bool:
return code == self.__code
def __is_blocked(self) -> bool:
if self.__block_until is None:
return False
return datetime.now() < self.__block_until
def __remaining_block_time(self) -> str:
remaining = self.__block_until - datetime.now()
seconds = int(remaining.total_seconds())
minutes, sec = divmod(seconds, 60)
return f"Door is blocked. Try again in {minutes} min {sec} sec."
def __check_block(self) -> None:
if self.__failed_attempts >= self.__max_attempts:
self.__block_until = datetime.now() + self.__block_duration
print("Too many failed attempts.")
def __handle_failed_attempt(self) -> None:
self.__failed_attempts += 1
self.__check_block()
def __reset_attempts(self) -> None:
self.__failed_attempts = 0
# Пример использования
door = Door("1234", max_attempts=2, block_minutes=0.05)
try:
door.unlock("1111")
except DoorBlockedError as e:
print(e)
try:
door.change_code("0000", "9999")
except DoorBlockedError as e:
print(e)
try:
door.unlock("1234")
except DoorBlockedError as e:
print(e)
time.sleep(5)
try:
door.unlock("1234")
except DoorBlockedError as e:
print(e)
Логика решения: вместо того чтобы печатать сообщение о блокировке внутри класса, мы бросаем исключение с текстом. Это разделяет ответственность: класс Door сигнализирует о проблеме, а вызывающий код решает, как её обработать. Исключение наследуется от Exception, поэтому его можно отловить отдельно от других ошибок.
Проверить по документации: о
datetime см. datetime — Basic date and time types, об исключениях — Errors and Exceptions.