Продолжим разработку класса 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)
Сергей, добрый вечер.
Можете, пожалуйста, подсказать, в чем может быть ошибка? Не проходит 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}')"Вопрос отменяется, ошибку нашел, исправил и сдал.
Я писал вам ответ и прервался на занятие.
Подскажите, какую ошибку исправили?
Была ошибка на уровне идеи в реализации методов 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}')"Интересно что в старом коде в результате еще и операции деления /= могли давать неправильные результаты. Сама по себе потеря знака не решала проблему прохождения теста. Я почему и спросил какую ошибку исправили. Например, a = 5/2 b = 4/3, a /= b 5/3. Но судя по всему была еще какая-то ошибка, которую я отловить не успел, потому что устранение этих двух, приводило к тому, что тест падал на 9 шаге. А боле подробно посмотреть времени не хватило.
Если речь идет о неправильной реализации одного из фундаментальных методов, то это объясняет почему ошибка вылезала в самых разных порой неожиданных местах.
Вы имели в виду операцию *= ?
Да, с ней получается не тот ответ, так как, опять же, я не учитываю новый знаменатель после обновления числителя. Спасибо, что указали на эту ошибку.
Как всегда сработал метод: стереть все и заново написать решение.