Возможно, вы уже заметили, что дробные числа (float
) недостаточно точные для некоторых задач. Для более точных математических расчётов иногда прибегают к созданию правильных рациональных дробей, описываемых числителем и знаменателем.
Начнём разработку класса Fraction
, который реализует предлагаемые дроби.
Предусмотрите возможность инициализации дроби с помощью двух целых чисел или строки в формате <числитель>/<знаменатель>
.
В случаях наличия общего делителя у числителя и знаменателя, дробь следует сократить.
А также реализуйте методы:
numerator()
— возвращает значение числителя;numerator(number)
— изменяет значение числителя и производит сокращение дроби, если это необходимо;denominator()
– возвращает значение знаменателя;denominator(number)
— изменяет значение знаменателя и производит сокращение дроби, если необходимо;__str__
— возвращает строковое представление дроби в формате<числитель>/<знаменатель>
;__repr__
— возвращает описание объекта в форматеFraction(<числитель>, <знаменатель>)
.
Примечание
Будем считать, что пользователь знает о запрете деления на ноль.
Все числа в данной задаче будут положительными.
Все поля и методы, не требуемые в задаче, следует инкапсулировать (называть с использованием ведущих символов нижнего подчёркивания).
Ваше решение должно содержать только классы и функции.
В решении не должно быть вызовов инициализации требуемых классов.
Пример
Ввод
fraction = Fraction(3, 9)
print(fraction, repr(fraction))
fraction = Fraction('7/14')
print(fraction, repr(fraction))
Вывод
1/3 Fraction(1, 3)
1/2 Fraction(1, 2)
Ввод
fraction = Fraction(3, 210)
print(fraction, repr(fraction))
fraction.numerator(10)
print(fraction.numerator(), fraction.denominator())
fraction.denominator(2)
print(fraction.numerator(), fraction.denominator())
Вывод
1/70 Fraction(1, 70)
1 7
1 2
Решение
Нам предлагают создать класс Fraction, аналогичный существующему в python.
В первом задании все достаточно прозаично – от нас требуют написать представление нашей дробы (__str__ и __repr__)б что мы уже умеем делать после выполнения серии задач про классную точку. Помимо представления класса нам потребуется написать методы, позволяющие задать или получить значения числителя и знаменателя. Кроме того, нужно не забывать, что 2/4 это 1/2, и значит, нам надо уметь сокращать дроби. Для этого нам потребуется вспомнить как искать наибольший общий делитель, чтобы правильно сократить дробь.
И пусть вас не смущает то, что наш класс будет называться аналогично существующему в python. При том, что они довольно похожи, у наших классов будут и различия. В этой адаче, например наши методы должны уметь менять числитель и знаменатель, в то время как в стандартной реализации одноименные методы только возвращают соотвествуюшие значения. Задание новых возможно только путем создания новых объектов.
Итак, помимо методов, которые мы должны описать, мы создаем метод __gcd для поиска НОД и метод __reduction() для сокращения дроби.
Посмотреть код
Решение
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 __gcd(self, a, b):
while b != 0:
a, b = b, a % b
return abs(a)
def __reduction(self):
gcd = self.__gcd(self.__num, self.__den)
self.__num = self.__num // gcd
self.__den = self.__den // gcd
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):
if len(args):
self.__num = args[0]
self.__reduction()
return self.__num
def denominator(self, *args):
if len(args):
self.__den = args[0]
self.__reduction()
return self.__den
Не пойму в чём разница?
Вот так Яндекс не принимает:
class Fraction:
def __init__(self, *args):
if isinstance(args[0], str):
self.a, self.b = [int(x) for x in args[0].split(‘/’)]
else:
self.a, self.b = [x for x in args]
self.nod()
def nod(self):
x = self.a
y = self.b
while y != 0:
x, y = y, x % y
self.a = self.a // x
self.b = self.b // x
def __str__(self) -> str:
return f'{self.a}/{self.b}’
def __repr__(self) -> str:
return f’Fraction({self.a}, {self.b})’
Так всё устраивает:
class Fraction:
def __init__(self, *args):
if isinstance(args[0], str):
self.a, self.b = [int(x) for x in args[0].split(‘/’)]
else:
self.a, self.b = [x for x in args]
self.__nod()
def __nod(self):
x = self.a
y = self.b
while y != 0:
x, y = y, x % y
self.a = self.a // x
self.b = self.b // x
def __str__(self) -> str:
return f'{self.a}/{self.b}’
def __repr__(self) -> str:
return f’Fraction({self.a}, {self.b})’
def numerator(self, *args):
if args:
self.a = args[0]
self.__nod()
return self.a
def denominator(self, *args):
if args:
self.b = args[0]
self.__nod()
return self.b
Не нашёл объяснения. В чём разница в объявлении приватного метода, кроме того, что мы не можем его вне класса вызвать? Локально работает и так и так, но Яндекс ругается на:
Completion status: ABNORMAL_EXIT
Term sig: null
Error code: 2
Не понимаю логику…
Просто у них такое условие
Из текста задачи:
Все поля и методы, не требуемые в задаче, следует инкапсулировать (называть с использованием ведущих символов нижнего подчёркивания).
В целом приучают писать классы безопасно. Так вы не перегрузите gcd если она уже будет в другом коде.