282 lines
13 KiB
Python
282 lines
13 KiB
Python
|
"""
|
|||
|
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()
|