Объектно-ориентированное программирование, основы

Класс “время”

Создайте указанный ниже класс и отдельно напишите код, который проверяет работу каждого сделанного пункта.

  1. Заведите класс Time, который хранит информацию о количестве часов и минут. Т.е. введите два поля.
  2. Добавьте конструктор, в котором можно сразу указать количество часов и минут.
  3. Сделайте метод show();, который возвращает строку с часами и минутами через двоеточие. Например, он может вернуть "09:10".
  4. Сделайте методы is_day(), is_morning(), is_evening(), is_night(), которые возвращают верно ли, что сейчас, соответственно, день, утро, вечер, ночь. Придумайте сами, начиная с какого времени утро переходит в день и т.д.
  5. Сделайте метод say_hello(), который возвращает строку “Доброе утро”, “Добрый день” и т.п. в зависимости от текущего времени.
  6. Сделайте метод, который добавляет указанное количество минут: c.add_update(20). Если было "9:10", то должно получиться "9:30". А если было "9:50", должно получиться "10:10". Еще пример. "9:20" плюс 120 минут должно быть "11:20".
  7. Сделайте метод add, аналогичный предыдущему, но он должен возвращать новый объект класса Time, не изменяя текущий.
  8. Сделайте метод __iadd__, который делает ровно то же самое, что метод add_update. Но попробуйте вызвать его так не так c.__iadd__(20), а так c += 20.

Три последних метода делают почти одно и то же, убедитесь, что вы не пишете три раза один и тот же код.

Класс “Рациональные числа”

Сделаем класс Rational, это рациональные числа, для которых хранится их числитель и знаменатель. С рациональными числами можно совершать арифметические операции.

  1. Создайте класс и добавьте поля _n и _d (numerator, denominator - числитель, знаменатель).
  2. Реализайте метод __init__, чтобы можно было создавать объекты с помощью Rational(3, 2) и Rational(3). Для этого сделайте последний аргумент метода __init__ равным по-умолчанию 1.
  3. Добавьте метод __str__(), который возвращает естественное представление числа в виде строки, например, "5/7". Если знаменатель равен 1, его не нужно писать. Если числитель ноль, то "0", а не "0/2" и т.п.
  4. Добавьте метод as_number(), который возвращает значение в виде числа.
  5. Добавьте сокращение дроби в конструктор: поделить числитель и знаменатель на НОД, убедиться, что знаменатель положительный. Найдите в стандартной библиотеке python функцию, которая вычисляет НОД — не пишите его самостоятельно.
  6. Арифметические функции. add, sub, mul, div:

     r1 = r2.add(r3);  # создает новое число
     r2.add_in_place(r3);  # добавляет к r2 и изменяет его
    

    Протестируйте! Проверьте, например, что \(\frac16 + \frac13 = \frac12\), и проверьте другие аналогичные равенства для других арифметических операций.

  7. Замените методы арифметических операций на магические методы: __add__, __sub__, __mul__, __truediv__. in-place методы замените на __iadd__, __isub__, __imul__, __itruediv__. Проверьте, что эти методы можно вызывать и как r2.__add__(r3), и как r2 + r3, и как r2.__iadd__(r3), и как r2 += r3. Еще можно по желанию сделать метод __pow__, __ipow__ для возведения в степень.
  8. Перегрузите операторы сравнения
     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)
    

    Подумайте, что сравнения выражаются друг через друга. Достаточно реализовать, например, == и <, все остальное вы можете реализовать только через них.

  9. Создайте функцию не в классе Rational, которая по n считает \(1 + \frac12 + \frac13 + \frac14 + \frac15 + … + \frac1n\) Проверьте, что f(1) = 1, f(2) = 1.5, f(3) = 1.833333333333333
  10. Добавьте get- методы для числителя и знаменателя.
  11. Создайте статические константы ONE и ZERO типа Rational, хранящие элементы 0, 1 в виде рациональных чисел.
  12. Создайте статическую функцию для генерации всех несократимых дробей: irreducible(n) должно возвращать список (или лучше генератор) всех правильных несократимых дробей с указанным знаменателем. Например, irreducible(1) возвращает [0/1], а irreducible(10) возвращает [1/10, 3/10, 7/10, 9/10].
  13. Расширьте методы арифметических операций так, чтобы в качестве второго аргумента они принимали обычные целые числа. Кроме того, реализуйте методы типа __radd__, чтобы число могло быть первым аргументом, например, 1 + r вызовет r.__radd__(1).

Рисование

Класс Drawing (изображение) хранит изображение в виде списка списков символов. Например,

[
    ['.', '.', '.', '.', '.', '.', '.', '.', '.'],
    ['.', '.', '.', '.', '.', '.', '.', '.', '.'],
    ['.', '.', '.', '.', 'x', '.', '.', '.', '.'],
    ['.', '.', '.', '.', '.', '.', '.', '.', '.']
]
  1. Создайте инициализатор, в котором указывается размер (сколько строк и столбцов) и символ, которым изначально все заполнить. Например, Drawing(20, 30, '.').
  2. Создайте метод print(), он печатает на экране изображение.
  3. Метод set_point(x, y, char) рисует один символ в изображении. Методу нужно указать, где и какой символ поставить. Давайте использовать традиционную систему координат. x=0, y=0 должно соответствовать самой левой нижней точке. Увеличение x перемещает точку вправо. Увеличение y — вверх.
  4. Методы draw_vertical_line(...), draw_horizontal_line(...) рисуют вертикальную или горизонтальную линию от заданной точки до заданной. Определите сами параметры для методов, они должны определять расположение линии и символ, которым линию рисовать.
  5. Метод draw_rectangle() рисует прямоугольник по двум противоположным углам, стороны прямоугольника вертикальны и горизонтальны. Используйте методы, реализованные ранее.
  6. Необязательно: Рисование произвольной линии, используйте алгоритм Брезенхема, возьмите его из википедии или придумайте сами.
  7. Рисование круга (или окружности, но тогда с помощью алгоритма Брезенхема — это необязательно). Пользователь указывает центр и радиус, программа должна пройтись по всем точкам поля и, если они лежат в круге, зарисовать их указанным символом.
  8. Реализуйте метод draw(x, y, d) нарисовать изображение d в указанных координатах x, y. Координаты задают положение левого нижнего угла изображения.
  9. (Необязательная задача). Сделайте Drawing неизменяемым: все функции рисования возвращают новый Drawing и не меняют текущий. Чтобы не портить старый класс, назовите новый ImmutableDrawing.
  10. Используйте все реализованные вами ранее методы, чтобы нарисовать Джоконду. Ну или домик.