py_stepik/mod_oop/3.1_07_course_model.py

282 lines
13 KiB
Python
Raw Normal View History

2024-04-10 09:59:16 +00:00
"""
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()