E. Дроби v0.2

Продолжим разработку класса Fraction, который реализует предлагаемые дроби.

Предусмотрите возможность задать отрицательные числитель и/или знаменатель. А также перепишите методы __str__ и __repr__ таким образом, чтобы информация об объекте согласовывалась с инициализацией строкой.

Далее реализуйте оператор математического отрицания — унарный минус.

Примечание

Будем считать, что пользователь знает о запрете деления на ноль.
Все числа в данной задаче будут положительными.
Все поля и методы, не требуемые в задаче, следует инкапсулировать (называть с использованием ведущих символов нижнего подчёркивания).

Ваше решение должно содержать только классы и функции.
В решении не должно быть вызовов инициализации требуемых классов.

Пример

Ввод

a = Fraction(1, 3)
b = Fraction(-2, -6)
c = Fraction(-3, 9)
d = Fraction(4, -12)
print(a, b, c, d)
print(*map(repr, (a, b, c, d)))

Вывод

1/3 1/3 -1/3 -1/3
Fraction('1/3') Fraction('1/3') Fraction('-1/3') Fraction('-1/3')

Ввод

a = Fraction('-1/2')
b = -a
print(a, b, a is b)
b.numerator(-b.numerator())
a.denominator(-3)
print(a, b)
print(a.numerator(), a.denominator())
print(b.numerator(), b.denominator())

Вывод

-1/2 1/2 False
1/3 -1/2
1 3
1 2

Решение

Мы должны слегка модифицировать методы __str__ и __repr__. Кроме того, нам предлагают предусмотреть возможность задания отрицательных числителя и/или знаменателя. И тут мы попадаюм в ловушку произвола Яндекса при постановке задачи. Дело в том, что несмотря на то, что -(1/2), (-1)/2 и 1/(-2) по сути одна и та же дробь. Но в стандартном Fraction, отрицательнй знак принадлежит числителю, а знаменатель всегда положительный. В моей молодости в школе нас тоже призывали минус всегда переносить в числитель – это сильно упрощает все дальнейшие вычисления.

Из этого нюанса вытекает главная сложность этого задания. Когда учащиеся пытаются реализовать метод, изменяющий числитель, они получают ошибку, потому что Яндекс считает, что числитель дроби -1/2 равен единице. Хотя для большинства из нас и для стандартного модуля Fractions он будет равен -1. Соответственно, при замене числителя в отрицательной дроби важно не забыть учесть ее бывший знак, как того хочет Яндекс.

Я советую оставить знак в числителе, так как это достаточно сильно облегчит вам дальнейшие вычисления.

У меня для примирения сложившейся практики и требований Яндекса введено две модификации – добавлен метод __sign, который возвращает текущий знак дроби, проверяя числитель и внесено изменение в метод numerator для того, чтобы он всегда возвращал только положительное число. Напоминаю, что в стандартном модуле этот метод вернет отрицательное число, в случае если дробь отрицательная.

Посмотреть код

Решение

Python
class Fraction():
    def __init__(self, *args) -> None:
        if isinstance(args[0], str):
            self.__num, self.__den = [int(c) for c in args[0].split('/')]
        else:
            self.__num = args[0]
            self.__den = args[1]
        self.__reduction()

    def __sign(self):
        return -1 if self.__num < 0 else 1

    def __neg__(self) -> 'Fraction':
        return Fraction(-self.__num, self.__den)

    def __gcd(self, a, b) -> int:
        while b:
            a, b = b, a % b
        return abs(a)

    def __reduction(self) -> 'Fraction':
        __gcd = self.__gcd(self.__num, self.__den)
        self.__num //= __gcd
        self.__den //= __gcd

        if self.__den < 0:
            self.__num = -self.__num
            self.__den = abs(self.__den)
        return self

    def __str__(self) -> str:
        return f'{self.__num}/{self.__den}'

    def __repr__(self) -> str:
        return f"Fraction('{self.__num}/{self.__den}')"

    def numerator(self, *args) -> int:
        if len(args):
            self.__num = args[0] * self.__sign()
            self.__reduction()
        return abs(self.__num)

    def denominator(self, *args) -> int:
        if len(args):
            self.__den = args[0]
        self.__reduction()
        return abs(self.__den)
Подписаться
Уведомить о
guest
12 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
nmalkam
26.01.2024 15:46

в чём то же должен быть смысл не принимать числитель дроби -1/2 равен единице

находить решение из не стандартных ситуаций

Никита
Никита
13.02.2024 12:16

Добрый день, у Вас метод __neg__ возвращает значения с отрицательным числителем, при этом по коду он не нужен и нигде не вызывается

Никита
Никита
Ответить на  Сергей Клочко
14.02.2024 10:12

Да, я проверил, без него не работает, однако я не понимаю, почему он работает, если этот метод не вызывается

Никита
Никита
Ответить на  Сергей Клочко
15.02.2024 10:39

А, я понял. Спасибо большое!

Алексей
Алексей
05.09.2024 17:17

Уважаемый Сергей, здравствуйте,
После длительного перерыва продолжил решать, подглядываю в Ваши ответы.
Объясните (или подскажите, где посмотреть), что это и зачем?
def __init__(self, *args) -> None: – такое (-> None), почему-то Вы пишите всегда
def __str__(self) -> str: -внесли в Дроби V0.1, у Яндекса и в других решениях нет-> str
def __gcd(self, a, b) -> int: – в первой задачке про Дроби -> int не было
def __reduction(self) -> ‘Fraction’: – аналогичный вопрос.

Где-то (возможно, у Вас видел про -> None), не могу найти теперь.
Посмотрел в ресурсе, где раньше (пока Ваш не нашел) подглядывал ответы – https://github.com/Pavellver/Yandex_handbook_answers/. Там таких конструкций нет.

Что это за конструкции, зачем они нужны, почему Вы их где-то применяете, а где-то – нет?
Спасибо.

Алексей
Алексей
Ответить на  Сергей Клочко
11.09.2024 12:45

Сергей,
Спасибо за разъяснения.

Ольга
Ольга
24.10.2024 14:19

Сергей, здравствуйте.
Скажите, пожалуйста, почему класс указан как Fraction()?
В смысле, не class Fraction, а class Fraction()?
И в прошлой задаче было так… Что означают скобки?

Ольга
Ольга
Ответить на  Сергей Клочко
24.10.2024 15:40

А, да-да, если вдруг будет наследование. Заранее подготовились, так сказать. На всякий случай. Возьму “на вооружение”.
Спасибо большое за как всегда подробное и очень доходчивое объяснение!