py_stepik/mod_oop/3.1_07_course_model.py
2024-04-11 10:00:44 +03:00

282 lines
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
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()