""" https://stepik.org/lesson/701986/step/7?unit=702087 Необходимо создать программу для обучающего курса. Для этого объявляются три класса: Course - класс, отвечающий за управление курсом в целом; Module - класс, описывающий один модуль (раздел) курса; LessonItem - класс одного занятия (урока). Объекты класса LessonItem должны создаваться командой: lesson = LessonItem(название урока, число практических занятий, общая длительность урока) >>> lesson = LessonItem("название урока", 2, 10) Соответственно, в каждом объекте класса LessonItem должны создаваться локальные атрибуты: title - название урока (строка); practices - число практических занятий (целое положительное число); duration - общая длительность урока (целое положительное число). Необходимо с помощью магических методов реализовать следующую логику взаимодействия с объектами класса LessonItem: 1. Проверять тип присваиваемых данных локальным атрибутам. Если типы не соответствуют требованиям, то генерировать исключение командой: raise TypeError("Неверный тип присваиваемых данных.") 2. При обращении к несуществующим атрибутам объектов класса LessonItem возвращать значение False. 3. Запретить удаление атрибутов title, practices и duration в объектах класса LessonItem. --- тесты типов --- >>> LessonItem(1, 2, 10) Traceback (most recent call last): ... TypeError: Неверный тип присваиваемых данных. >>> LessonItem("название урока", -2, 10) Traceback (most recent call last): ... TypeError: Неверный тип присваиваемых данных. >>> LessonItem("название урока", 2, -10) Traceback (most recent call last): ... TypeError: Неверный тип присваиваемых данных. ------------------- Объекты класса Module должны создаваться командой: >>> module = Module("название модуля") Каждый объект класса Module должен содержать локальные атрибуты: name - название модуля; lessons - список из уроков (объектов класса LessonItem), входящих в модуль (изначально список пуст). Также в классе Module должны быть реализованы методы: add_lesson(self, lesson) - добавление в модуль (в конец списка lessons) нового урока (объекта класса LessonItem); remove_lesson(self, indx) - удаление урока по индексу в списке lessons. Наконец, объекты класса Course создаются командой: course = Course(название курса) И содержат следующие локальные атрибуты: name - название курса (строка); modules - список модулей в курсе (изначально список пуст). Также в классе Course должны присутствовать следующие методы: add_module(self, module) - добавление нового модуля в конце списка modules; remove_module(self, indx) - удаление модуля из списка modules по индексу в этом списке. Пример использования классов (в программе эти строчки не писать): course = Course("Python ООП") module_1 = Module("Часть первая") module_1.add_lesson(LessonItem("Урок 1", 7, 1000)) module_1.add_lesson(LessonItem("Урок 2", 10, 1200)) module_1.add_lesson(LessonItem("Урок 3", 5, 800)) course.add_module(module_1) module_2 = Module("Часть вторая") module_2.add_lesson(LessonItem("Урок 1", 7, 1000)) module_2.add_lesson(LessonItem("Урок 2", 10, 1200)) course.add_module(module_2) P.S. На экран ничего выводить не нужно. >>> def make_course(): ... course = Course("Python ООП") ... module_1 = Module("Часть первая") ... module_1.add_lesson(LessonItem("Урок 1", 7, 1000)) ... module_1.add_lesson(LessonItem("Урок 2", 10, 1200)) ... module_1.add_lesson(LessonItem("Урок 3", 5, 800)) ... course.add_module(module_1) ... module_2 = Module("Часть вторая") ... module_2.add_lesson(LessonItem("Урок 1", 7, 1000)) ... module_2.add_lesson(LessonItem("Урок 2", 10, 1200)) ... course.add_module(module_2) ... return course >>> ((course := make_course()).name, { ... module.name: [ ... f"{lesson.title}, {lesson.practices}, {lesson.duration}" ... for lesson in module.lessons ... ] ... for module in course.modules ... }) ('Python ООП', {'Часть первая': ['Урок 1, 7, 1000', 'Урок 2, 10, 1200', 'Урок 3, 5, 800'], 'Часть вторая': ['Урок 1, 7, 1000', 'Урок 2, 10, 1200']}) """ class LessonItem: title: str practices: int duration: int class Validators: @staticmethod def is_positive(value) -> bool: return value > 0 @staticmethod def olways_ok(_) -> bool: return True __nodel = "title", "practices", "duration" __validators = { "practices": Validators.is_positive, "duration": Validators.is_positive, } def __init__(self, title: str, practices: int, duration: int): self.title, self.practices, self.duration = title, practices, duration def __getattr__(self, name: str): return False def __setattr__(self, name: str, value): if not ( isinstance(value, self.__annotations__.get(name, object)) and self.__validators.get(name, self.Validators.olways_ok)(value) ): raise TypeError("Неверный тип присваиваемых данных.") super().__setattr__(name, value) def __delattr__(self, name: str): if name in self.__nodel: raise AttributeError(f"Нельзя удалить атрибут {name}") class Module: def __init__(self, name: str): self.name, self.lessons = name, [] def add_lesson(self, lesson: LessonItem): self.lessons.append(lesson) def remove_lesson(self, indx: int): del self.lessons[indx] class Course: def __init__(self, name: str): self.name, self.modules = name, [] def add_module(self, module: Module): self.modules.append(module) def remove_module(self, indx: int): del self.modules[indx] def tests(): # из коммента https://stepik.org/lesson/701986/step/7?discussion=7381286&unit=702087 # TEST-TASK___________________________________ course = Course("Python ООП") assert type(course.name) is str, "название курса должно быть строкой" assert ( type(course.modules) is list and len(course.modules) == 0 ), "modules должен быть списком, изначально список пуст" # add_module(self, module) - добавление нового модуля в конце списка modules; # remove_module(self, indx) - удаление модуля из списка modules по индексу в этом списке. assert hasattr(course, "add_module"), "add_module необъявлен" assert hasattr(course, "remove_module"), "remove_module необъявлен" # module_1 = Module("Часть первая") module_2 = Module("Часть вторая") assert type(module_1.name) is str, "название модуля должно быть строкой" assert ( type(module_1.lessons) is list and len(module_1.lessons) == 0 ), "lesson должен быть списком, изначально список пуст" # add_lesson(self, lesson) - добавление в модуль (в конец списка lessons) нового урока (объекта класса LessonItem); # remove_lesson(self, indx) - удаление урока по индексу в списке lessons. assert hasattr(module_1, "add_lesson"), "add_lesson необъявлен" assert hasattr(module_1, "remove_lesson"), "remove_lesson необъявлен" # les_1 = LessonItem("Урок 1", 7, 1000) les_2 = LessonItem("Урок 2", 10, 1200) assert type(les_1.title) is str, "название урока должно быть строкой" assert ( type(les_1.practices) is int and les_1.practices > 0 ), "practices должен быть целым числом больше ноля" assert ( type(les_1.duration) is int and les_1.practices > 0 ), "duration должен быть целым положительным числом" # # проверка методов course.add_module(module_1) course.add_module(module_2) assert ( len(course.modules) == 2 and course.modules[1] == module_2 ), "некоректно отработал метод add_module" course.remove_module(0) assert ( module_1 not in course.modules and len(course.modules) == 1 ), "некоректно отработал метод remove_module" # module_1.add_lesson(les_1) module_1.add_lesson(les_2) assert ( len(module_1.lessons) == 2 and module_1.lessons[1] == les_2 ), "некоректно отработал метод add_lesson" module_1.remove_lesson(0) assert ( les_1 not in module_1.lessons and len(module_1.lessons) == 1 ), "некоректно отработал метод remove_lesson" # # проверка методов - LessonItem # 1. Проверять тип присваиваемых данных локальным атрибутам. Если типы не соответствуют требованиям, то генерировать исключение командой: # raise TypeError("Неверный тип присваиваемых данных.") try: les_3 = LessonItem(3, 8, 900) except TypeError: assert True else: assert ( False ), "не сгенерировалось исключение TypeError при записи некорректных данных в title" try: les_3 = LessonItem("Урок 2", 8.0, 900) except TypeError: assert True else: assert ( False ), "не сгенерировалось исключение TypeError при записи некорректных данных в practices" try: les_3 = LessonItem("Урок 2", 8, 900, 0) except TypeError: assert True else: assert ( False ), "не сгенерировалось исключение TypeError при записи некорректных данных в duration" # 2. При обращении к несуществующим атрибутам объектов класса LessonItem возвращать значение False. les_4 = LessonItem("Урок 2", 8, 900) assert hasattr( les_1, "__getattr__" ), "ошибка при обращении к несуществующему локальному атрибуту, метод должен вернуть False" assert ( les_4.value_not_atr is False ), "ошибка при обращении к несуществующему локальному атрибуту, метод должен вернуть False" # 3. Запретить удаление атрибутов title, practices и duration в объектах класса LessonItem. assert hasattr( les_4, "__delattr__" ), "возможно вы не продумали запрет на удаление локальных атрибутов - title, practices и duration" try: del les_4.title del les_4.practices del les_4.duration except: ... else: if any( True if _ not in les_4.__dict__ else False for _ in ["title", "practices", "duration"] ): print( "Ошибка при удалении локальных атрибутов - title, practices и duration" ) if __name__ == "__main__": import doctest doctest.testmod() tests()