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

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

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

  1. Заведите класс Time, который хранит информацию о количестве часов и минут. Т.е. добавьте конструктор, в котором можно сразу указать количество часов и минут.
  2. Сделайте метод show();, который возвращает строку с часами и минутами через двоеточие. Например, он может вернуть "09:10".
  3. Сделайте методы isDay(), isMorning(), isEvening(), isNight(), которые возвращают верно ли, что сейчас, соответственно, день, утро, вечер, ночь. Придумайте сами, начиная с какого времени утро переходит в день и т.п.
  4. Сделайте метод sayHello(), который возвращает строку “Доброе утро”, “Добрый день” и т.п. в зависимости от текущего времени.
  5. Сделайте метод, который добавляет указанное количество минут: c.add(20). Если было "9:10", то должно получиться "9:30". А если было "9:50", должно получиться "10:10". Еще пример. "9:20" плюс 120 минут должно быть "11:20".

ASCI-графика

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

[
    ['.', '.', '.', '.', '.', '.', '.', '.', '.'],
    ['.', '.', '.', '.', '.', '.', '.', '.', '.'],
    ['.', '.', '.', '.', 'x', '.', '.', '.', '.'],
    ['.', '.', '.', '.', '.', '.', '.', '.', '.']
]

Это соответствует картинке:

.........
.........
....x....
.........
  1. Создайте конструктор, в котором указывается размер (сколько строк и столбцов) и символ, которым изначально все заполнить. Например, Drawing(20, 30, '.').
  2. Создайте метод print(), он печатает на экране изображение
  3. Метод setPoint(x, y, char) рисует один символ в изображении. Методу нужно указать, где и какой символ поставить.
  4. Методы drawVerticalLine(...), drawHorizontalLine(...) рисуют вертикальную или горизонтальную линию от заданной точки до заданной. Определите сами параметры для методов, они должны определять расположение линии и символ, которым линию рисовать.
  5. Метод drawRectangle() рисует прямоугольник по двум противоположным углам, стороны прямоугольника вертикальны и горизонтальны. Используйте методы, реализованные ранее.
  6. Необязательно: Рисование произвольной линии (найдите в википедии алгоритм Брезенхема. Если википедия заблокирована, можно не решать)
  7. Рисование круга (или окружности методом Брезенхема — необязательно). Пользователь указывает центр и радиус, программа должна пройтись по всем точкам поля и, если они лежат в круге, зарисовать их указанным символом.
  8. Реализуйте метод draw(x, y, drawing) нарисовать одно изображение на другом. При вызове метода указывается, где и какое изображение рисовать.
  9. (Необязательная задача). Сделайте Drawing неизменяемым: все функции рисования возвращают новый Drawing и не меняют текущий. Чтобы не портить старый класс, назовите новый ImmutableDrawing.
  10. Используйте все реализованные вами ранее методы, чтобы нарисовать Джоконду. Ну или домик.

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

Сделаем класс 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. Сделайте так, чтобы числитель и знаменатель дроби стали приватными (название начинается с подчеркивания) и создайте методы, которые возвращают значение числителя и знаменателя.
  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).