G. Дроби v0.4

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

Реализуйте бинарные операторы:

  • * — умножение дробей, создаёт новую дробь;
  • / — деление дробей, создаёт новую дробь;
  • *= — умножение дробей, изменяет дробь, переданную слева;
  • /= — деление дробей, изменяет дробь, переданную слева.

Также разработайте метод reverse, возвращающий дробь обратную данной.

Примечание

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

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

Пример

Ввод

a = Fraction(1, 3)
b = Fraction(1, 2)
c = a * b
print(a, b, c, a is c, b is c)

Вывод

1/3 1/2 1/6 False False

Ввод

a = Fraction(1, 3)
c = b = Fraction(2, 1).reverse()
b /= a
print(a, b, c, b is c)

Вывод

1/3 3/2 3/2 True

Решение

Продолжаем наращивать “вычислительную мощность” нашего класса. Добавляем умножение и деление.

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

Решение

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 __add__(self, other) -> 'Fraction':
        common_denominator = self.__den * other.__den
        new = Fraction(1, 1)
        new.__num = self.__num * other.__den + other.__num * self.__den
        new.__den = common_denominator
        new.__reduction()
        return new

    def __sub__(self, other) -> 'Fraction':
        common_denominator = self.__den * other.__den
        new = Fraction(1, 1)
        new.__num = self.__num * other.__den - other.__num * self.__den
        new.__den = common_denominator
        new.__reduction()
        return new

    def __iadd__(self, other) -> 'Fraction':
        common_denominator = self.__den * other.__den
        self.__num = self.__num * other.__den + other.__num * self.__den
        self.__den = common_denominator
        self.__reduction()
        return self

    def __isub__(self, other) -> 'Fraction':
        common_denominator = self.__den * other.__den
        self.__num = self.__num * other.__den - other.__num * self.__den
        self.__den = common_denominator
        self.__reduction()
        return self

    def __mul__(self, other) -> 'Fraction':
        common_denominator = self.__den * other.__den
        new = Fraction(1, 1)
        new.__num = self.__num * other.__num
        new.__den = common_denominator
        new.__reduction()
        return new

    def __truediv__(self, other) -> 'Fraction':
        new = Fraction(self.__num, self.__den)
        new.__reduction()
        return new.__mul__(other.reverse())

    def __imul__(self, other) -> 'Fraction':
        common_denominator = self.__den * other.__den
        self.__num = self.__num * other.__num
        self.__den = common_denominator
        self.__reduction()
        return self

    def __itruediv__(self, other) -> 'Fraction':
        return self.__imul__(other.reverse())

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

    def __reduction(self) -> tuple:
        __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.__num, self.__den

    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)

    def reverse(self) -> 'Fraction':
        return Fraction(self.__den, self.__num)
Подписаться
Уведомить о
guest
6 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
Олег
Олег
20.02.2025 23:25

Сергей, добрый вечер.
Можете, пожалуйста, подсказать, в чем может быть ошибка? Не проходит 8 тест (WA).

class Fraction:
    def __init__(self, *args):
        if len(args) == 2:
            self.num, self.denom = args
        else:
            self.num, self.denom = list(map(int, args[0].split('/')))
        self.__reduction()
    
    def __get_sign(self):
        return 1 if self.num * self.denom > 0 else -1


    def numerator(self, number=None):
        if number is None:
            return abs(self.num)
        self.num = number * self.__get_sign()
        self.__reduction()


    def denominator(self, number=None):
        if number is None:
            return abs(self.denom)
        self.denom = number
        self.__reduction()


    def __reduction(self):
        gcd = self.__gcd(self.num, self.denom)
        self.num //= gcd
        self.denom //= gcd


        if self.denom < 0:
            self.denom = abs(self.denom)
            self.num *= -1


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


    def __add__(self, other):
        __new_num = self.num * other.denom + self.denom * other.num
        __new_denom = self.denom * other.denom
        return Fraction(__new_num, __new_denom)
    
    def __sub__(self, other):
        __new_num = self.num * other.denom - self.denom * other.num
        __new_denom = self.denom * other.denom
        return Fraction(__new_num, __new_denom)


    def __iadd__(self, other):
        new_num = self.num * other.denom + self.denom * other.num
        new_denom = self.denom * other.denom
        self.num = new_num
        self.denom = new_denom
        self.__reduction()
        return self
    
    def __isub__(self, other):
        new_num = self.num * other.denom - self.denom * other.num
        new_denom = self.denom * other.denom
        self.num = new_num
        self.denom = new_denom
        self.__reduction()
        return self


    def __mul__(self, other):
        new_num = self.num * other.num
        new_denom = self.denom * other.denom
        return Fraction(new_num, new_denom)
    
    def __truediv__(self, other):
        new_num = self.num * other.denom
        new_denom = self.denom * other.num
        return Fraction(new_num, new_denom)
    
    def __imul__(self, other):
        new_num = self.num * other.num
        new_denom = self.denom * other.denom
        self.numerator(new_num)
        self.denominator(new_denom)
        return self


    def __itruediv__(self, other):
        return self.__imul__(other.reverse())
    
    def reverse(self):
        return Fraction(self.denom, self.num)


    def __neg__(self):
        return Fraction(-self.num, self.denom)


    def __str__(self):
        return f'{self.num}/{self.denom}'
    
    def __repr__(self):
        return f"Fraction('{self.num}/{self.denom}')"


    
Олег
Олег
Ответить на  Олег
21.02.2025 13:15

Вопрос отменяется, ошибку нашел, исправил и сдал.

Олег
Олег
Ответить на  Сергей Клочко
21.02.2025 18:28

Была ошибка на уровне идеи в реализации методов imul и itrudive через методы numerator и denominator – не зря ведь мы их писали, и они как раз меняют числитель и знаменатель, и не хотелось напрямую влиять на атрибут объекта через присвавание, да и у них есть встроенный reduction. Но numerator также умножает полученный числитель на знак дроби, и соответственно меняет числитель в случае отрицательного знака дроби. Поэтому, видимо, в 8 тесте отрицательную дробь и умножили на отрицательную дробь, и должна была получиться положительная дробь. А у меня получалось положительное произведение, умноженное на отрицательный знак первой дроби (на момент вычисления он будет отрицательным). Как-то так.

Исправленный код прилагаю

class Fraction:
    def __init__(self, *args):
        if len(args) == 2:
            self.num, self.denom = args
        else:
            self.num, self.denom = list(map(int, args[0].split('/')))
        self.__reduction()
    
    def __get_sign(self):
        return 1 if self.num * self.denom > 0 else -1

    def numerator(self, number=None):
        if number is None:
            return abs(self.num)
        self.num = number * self.__get_sign()
        self.__reduction()

    def denominator(self, number=None):
        if number is None:
            return abs(self.denom)
        self.denom = number
        self.__reduction()

    def __reduction(self):
        gcd = self.__gcd(self.num, self.denom)
        self.num //= gcd
        self.denom //= gcd

        if self.denom < 0:
            self.denom = abs(self.denom)
            self.num *= -1

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

    def __add__(self, other):
        __new_num = self.num * other.denom + self.denom * other.num
        __new_denom = self.denom * other.denom
        return Fraction(__new_num, __new_denom)
    
    def __sub__(self, other):
        __new_num = self.num * other.denom - self.denom * other.num
        __new_denom = self.denom * other.denom
        return Fraction(__new_num, __new_denom)

    def __iadd__(self, other):
        new_num = self.num * other.denom + self.denom * other.num
        new_denom = self.denom * other.denom
        self.num = new_num
        self.denom = new_denom
        self.__reduction()
        return self
    
    def __isub__(self, other):
        new_num = self.num * other.denom - self.denom * other.num
        new_denom = self.denom * other.denom
        self.num = new_num
        self.denom = new_denom
        self.__reduction()
        return self

    def __mul__(self, other):
        new_num = self.num * other.num
        new_denom = self.denom * other.denom
        return Fraction(new_num, new_denom)
    
    def __truediv__(self, other):
        new_num = self.num * other.denom
        new_denom = self.denom * other.num
        return Fraction(new_num, new_denom)
    
    def __imul__(self, other):
        self.num = self.num * other.num
        self.denom = self.denom * other.denom
        self.__reduction()
        return self

    def __itruediv__(self, other):
        self.num = self.num * other.denom
        self.denom = self.denom * other.num
        self.__reduction()
        return self

    def reverse(self):
        return Fraction(self.denom, self.num)

    def __neg__(self):
        return Fraction(-self.num, self.denom)

    def __str__(self):
        return f'{self.num}/{self.denom}'
    
    def __repr__(self):
        return f"Fraction('{self.num}/{self.denom}')"

     
Олег
Олег
Ответить на  Сергей Клочко
21.02.2025 21:10

Вы имели в виду операцию *= ?
Да, с ней получается не тот ответ, так как, опять же, я не учитываю новый знаменатель после обновления числителя. Спасибо, что указали на эту ошибку.
Как всегда сработал метод: стереть все и заново написать решение.