Объектно-ориентированное программирование, основы
Класс “время”
Создайте указанный ниже класс и отдельно напишите код, который проверяет работу каждого сделанного пункта.
- Заведите класс
Time
, который хранит информацию о количестве часов и минут. Т.е. введите два поля. - Добавьте конструктор, в котором можно сразу указать количество часов и минут.
- Сделайте метод
show();
, который возвращает строку с часами и минутами через двоеточие. Например, он может вернуть"09:10"
. - Сделайте методы
is_day()
,is_morning()
,is_evening()
,is_night()
, которые возвращают верно ли, что сейчас, соответственно, день, утро, вечер, ночь. Придумайте сами, начиная с какого времени утро переходит в день и т.д. - Сделайте метод
say_hello()
, который возвращает строку “Доброе утро”, “Добрый день” и т.п. в зависимости от текущего времени. - Сделайте метод, который добавляет указанное количество минут:
c.add_update(20)
. Если было"9:10"
, то должно получиться"9:30"
. А если было"9:50"
, должно получиться"10:10"
. Еще пример."9:20"
плюс 120 минут должно быть"11:20"
. - Сделайте метод
add
, аналогичный предыдущему, но он должен возвращать новый объект класса Time, не изменяя текущий. - Сделайте метод
__iadd__
, который делает ровно то же самое, что методadd_update
. Но попробуйте вызвать его так не такc.__iadd__(20)
, а такc += 20
.
Три последних метода делают почти одно и то же, убедитесь, что вы не пишете три раза один и тот же код.
Класс “Рациональные числа”
Сделаем класс Rational, это рациональные числа, для которых хранится их числитель и знаменатель. С рациональными числами можно совершать арифметические операции.
- Создайте класс и добавьте поля
_n
и_d
(numerator, denominator - числитель, знаменатель). - Реализайте метод
__init__
, чтобы можно было создавать объекты с помощьюRational(3, 2)
иRational(3)
. Для этого сделайте последний аргумент метода__init__
равным по-умолчанию 1. - Добавьте метод
__str__()
, который возвращает естественное представление числа в виде строки, например,"5/7"
. Если знаменатель равен 1, его не нужно писать. Если числитель ноль, то"0"
, а не"0/2"
и т.п. - Добавьте метод as_number(), который возвращает значение в виде числа.
- Добавьте сокращение дроби в конструктор: поделить числитель и знаменатель на НОД, убедиться, что знаменатель положительный. Найдите в стандартной библиотеке python функцию, которая вычисляет НОД — не пишите его самостоятельно.
-
Арифметические функции. add, sub, mul, div:
r1 = r2.add(r3); # создает новое число r2.add_in_place(r3); # добавляет к r2 и изменяет его
Протестируйте! Проверьте, например, что \(\frac16 + \frac13 = \frac12\), и проверьте другие аналогичные равенства для других арифметических операций.
- Замените методы арифметических операций на магические методы:
__add__
,__sub__
,__mul__
,__truediv__
. in-place методы замените на__iadd__
,__isub__
,__imul__
,__itruediv__
. Проверьте, что эти методы можно вызывать и какr2.__add__(r3)
, и какr2 + r3
, и какr2.__iadd__(r3)
, и какr2 += r3
. Еще можно по желанию сделать метод__pow__
,__ipow__
для возведения в степень. - Перегрузите операторы сравнения
x == y __eq__(self, other) x != y __nq__(self, other) x < y __lt__(self, other) x <= y __le__(self, other) x > y __gt__(self, other) x >= y __ge__(self, other)
Подумайте, что сравнения выражаются друг через друга. Достаточно реализовать, например,
==
и<
, все остальное вы можете реализовать только через них. - Создайте функцию не в классе Rational, которая по n считает \(1 + \frac12 + \frac13 + \frac14 + \frac15 + … + \frac1n\) Проверьте, что f(1) = 1, f(2) = 1.5, f(3) = 1.833333333333333
- Добавьте get- методы для числителя и знаменателя.
- Создайте статические константы ONE и ZERO типа Rational, хранящие элементы 0, 1 в виде рациональных чисел.
- Создайте статическую функцию для генерации всех несократимых дробей:
irreducible(n)
должно возвращать список (или лучше генератор) всех правильных несократимых дробей с указанным знаменателем. Например,irreducible(1)
возвращает[0/1]
, аirreducible(10)
возвращает[1/10, 3/10, 7/10, 9/10]
. - Расширьте методы арифметических операций так, чтобы в качестве второго аргумента они принимали обычные целые числа. Кроме того, реализуйте методы типа
__radd__
, чтобы число могло быть первым аргументом, например,1 + r
вызоветr.__radd__(1)
.
Рисование
Класс Drawing (изображение) хранит изображение в виде списка списков символов. Например,
[
['.', '.', '.', '.', '.', '.', '.', '.', '.'],
['.', '.', '.', '.', '.', '.', '.', '.', '.'],
['.', '.', '.', '.', 'x', '.', '.', '.', '.'],
['.', '.', '.', '.', '.', '.', '.', '.', '.']
]
- Создайте инициализатор, в котором указывается размер (сколько строк и столбцов) и символ, которым изначально все заполнить. Например,
Drawing(20, 30, '.')
. - Создайте метод
print()
, он печатает на экране изображение. - Метод
set_point(x, y, char)
рисует один символ в изображении. Методу нужно указать, где и какой символ поставить. Давайте использовать традиционную систему координат. x=0, y=0 должно соответствовать самой левой нижней точке. Увеличение x перемещает точку вправо. Увеличение y — вверх. - Методы
draw_vertical_line(...)
,draw_horizontal_line(...)
рисуют вертикальную или горизонтальную линию от заданной точки до заданной. Определите сами параметры для методов, они должны определять расположение линии и символ, которым линию рисовать. - Метод
draw_rectangle()
рисует прямоугольник по двум противоположным углам, стороны прямоугольника вертикальны и горизонтальны. Используйте методы, реализованные ранее. - Необязательно: Рисование произвольной линии, используйте алгоритм Брезенхема, возьмите его из википедии или придумайте сами.
- Рисование круга (или окружности, но тогда с помощью алгоритма Брезенхема — это необязательно). Пользователь указывает центр и радиус, программа должна пройтись по всем точкам поля и, если они лежат в круге, зарисовать их указанным символом.
- Реализуйте метод
draw(x, y, d)
нарисовать изображениеd
в указанных координатах x, y. Координаты задают положение левого нижнего угла изображения. - (Необязательная задача). Сделайте Drawing неизменяемым: все функции рисования возвращают новый Drawing и не меняют текущий. Чтобы не портить старый класс, назовите новый ImmutableDrawing.
- Используйте все реализованные вами ранее методы, чтобы нарисовать Джоконду. Ну или домик.