Программирование на Python

Проектирование класса Python. Методы класса

Разбить на страницы
Показывать лекцию целиком

На прошлой лекции мы начали проектирование класса, определяющего тип данных - рациональные числа. На этом этапе мы определили поля класса, конструктор класса и переопределили функцию __str__, наследуемую от родительского класса object. Построенный код позволяет клиентам класса создавать объекты проектируемого типа и выводить на печать методом print значения объектов в приемлемом формате.

Приведу построенный код, который будем пополнять в этой лекции:

class classRational():
    """
    Класс Rational.
	/// определяет новый тип данных - рациональные числа и основные
	/// операции над ними - сложение, умножение, вычитание и деление.
	/// Рациональное число задается парой целых чисел (m,n) и изображается,
	/// обычно в виде дроби m/n. Число m называется числителем, а 
	/// n - знаменателем. Для каждого рационального числа существует 
	/// множество его представлений, например 1/2, 2/4, 3/6, 6/12 -
	/// задают одно и тоже рациональное число. Среди всех представлений
	/// можно выделить то, в котором числитель и знаменатель взаимно 
	/// несократимы. Такой представитель будет храниться в полях класса.
	/// Операции над рациональными числами определяются естественным 
	/// для математиков образом. 
    """
    #поля класса
    # int m - числитель
    # int n - знаменатель
    def __init__(self,  m = 0, n = 1):
        """
        Конструктор класса. Создает рациональное число
	      m/n со взаимно несократимыми
	      числителем и знаменателем. 
          Параметры m, n - целые числа.
          Значения по умолчанию задают 
          рациональный ноль.
        """    
        #приведение знака
        if n < 0:
            m, n = -m, - n
        #приведение к несократимой дроби
        a, b = m, n
        if a < 0: 
            a = -a
        if a < b:
            a, b = b, a
        while b != 0:
            a, b = b, a % b
        d = a
        self.m = m // d
        self.n = n // d
        
    def __str__(self):
        """
        Переопределение встроенной функции __str__
        """
        return str(self.m) + "/" + str(self.n) 

Перейдем теперь к заданию методов класса, определяющих поведение объектов. Для рациональных чисел это операции, которые можно выполнять над этими числами. Начнем со сложения и определим метод Plus, позволяющий сложить два рациональных числа и получить в результате новое рациональное число. Вот его определение на Python:

    def Plus(self, r):
        """
        Сложение рациональных чисел
        """
        u = self.m * r.n + self.n * r.m
        v = self.n * r.n
        return classRational(u, v)
    

У метода два аргумента. Первым аргументом является текущий объект, имя которого фиксировано - self. Напомню, в других языках - С++, C#, Java - фиксированное имя текущего объекта - это this. Аргумент r задает слагаемое, прибавляемое к текущему объекту. Тип аргумента не задается в Pythonи успех операции обеспечивается лишь в том случае, если у фактического аргумента, передаваемого методу Plus будут атрибуты m и n, в противном случае возникнет исключительная ситуация, требующая обработки в процессе выполнения. Успех гарантируется, если второй аргумент действительно является рациональным числом.

Метод Plus позволяет выполнять операции над рациональными числами в объектном стиле. Синтаксически значительно эффективнее перегрузить операцию "+". Вместо присваивания в форме:

$$r5 = r1.Plus(r2)$$

рекомендуется использовать форму, привычную при работе с числами:

$$r6 = r3 + r4.$$

Язык Pythonпозволяет выполнять перегрузку всех знаков операций, применяемых при работе с числами. Но поскольку перегрузки в языке Pythonнет, то перегрузка знаков операций моделируется переопределением методов, соответствующих каждому знаку операции. Покажем, как это делается, на примере операции "+":

  def __add__(self, r ):
        """
        Переопределение операции +
        """
        return self.Plus(r)
    

В определении использован созданный метод Plus. Клиент класса может по желанию использовать любую форму выполнения операции сложения. Вот пример:

def test5():    
    global r5, r6
    r5 = r1.Plus(r2)
    r6 = r3 + r4
    print("r5 = ", r5)
    print("r6 = ", r6)

Вот результаты работы двух тестов - test1, test5, создающих глобальные переменные: r1 - r6:

О перегрузке методов

Рассматривая метод сложения, нам, конечно, хотелось бы иметь вариант, когда к рациональному числу прибавляется целое число типа int. Обычное решение этой проблемы состоит в том, чтобы для этого варианта создать перегруженный метод с тем же именем Plus, но с аргументом другого типа - int, а не classRational. Однако, как мы знаем, перегрузки методов класса в Python нет, - проблему нужно решать другим способом. Такой способ есть. Он состоит в анализе типа аргумента. Конструкция if - elif, встроенная в метод, позволяет создать нужные варианты перегрузки. Воспользуемся этим способом и создадим новый вариант метода Plus. Старый метод сохраним, переименовав его в метод Plus_old. Вот новая версия метода Plus, позволяющая к рациональному числу прибавлять как рациональное число, так и целое число:

   def Plus(self, r):
        """
        Определение метода сложения рациональных чисел
        Аргумент r может быть рациональным или целым числом
        Для других типов аргумента возбуждается исключение
        """
        if type(r) == classRational:
            u = self.m * r.n + self.n * r.m
            v = self.n * r.n
            return classRational(u, v)
        elif type(r) == int:            
            u = self.m + self.n * r 
            v = self.n 
            return classRational(u, v)
        else:
     raise TypeError

Как видите, выполняется проверка типа аргумента. Для каждого возможного типа аргумента выполняются требуемые действия. Для аргумента недопустимого типа метод raise возбуждает исключение стандартного типа TypeError, уведомляющее о недопустимом типе. Исключение может быть перехвачено в программе и обработано соответствующим образом. Сейчас я не буду вдаваться в подробности работы с исключениями. Им будет посвящена отдельная лекция.

Заметьте, изменение метода Plus не отражается на определении операции "+". В нашем следующем примере будут строиться выражения, в которых операндами выступают как рациональные, так и целые числа:

def test6():
    global r5, r6
    r5 = r1 + r2 + r3
    r6 = r1 + 2 + r4
    print("r5 = ", r5)
    print("r6 = ", r6)
    #r7 = r1 + 5.5  

Вот результаты работы этого теста:

Заметьте, последний оператор присваивания в этом тесте закомментирован. Если его раскомментировать, то возникнет исключение. Поскольку перехват исключения не предусмотрен, то будет работать обработчик стандартного исключения, который выведет соответствующее сообщение об ошибке. Приведу текст сообщения об ошибке:

Мы подробно рассмотрели, как строить метод Plus, выполняющий сложение, как перегрузить соответствующую операцию "+". Приведем теперь уже без комментариев реализацию других арифметических операций - вычитания, умножения, деления:

    
def Minus(self, r):
        """
        Определение метода вычитания рациональных чисел
        Аргумент может быть рациональным или целым числом
        Для других типов аргумента возбуждается исключение
        """
        if type(r) == classRational:
            u = self.m * r.n - self.n * r.m
            v = self.n * r.n
            return classRational(u, v)
        elif type(r) == int:            
            u = self.m - self.n * r
            v = self.n 
            return classRational(u, v)
        else:
            raise TypeError
    def __sub__(self, r ):
        """
        Переопределение операции -
        """
        return self.Minus(r)


    def Mult(self, r):
        """
        Определение метода умножения рациональных чисел
        Аргумент может быть рациональным или целым числом
        Для других типов аргумента возбуждается исключение
        """
        if type(r) == classRational:
            u = self.m * r.m 
            v = self.n * r.n
            return classRational(u, v)
        elif type(r) == int:            
            u = self.m * r
            v = self.n 
            return classRational(u, v)
        else:
            raise TypeError
    def __mul__(self, r ):
        """
        Переопределение операции *
        """
        return self.Mult(r)



    def Div(self, r):
        """
        Определение метода деления рациональных чисел
        Аргумент может быть рациональным или целым числом
        Для других типов аргумента возбуждается исключение
        """
        if type(r) == classRational:
            u = self.m * r.n 
            v = self.n * r.m
            return classRational(u, v)
        elif type(r) == int:            
            u = self.m 
            v = self.n * r
            return classRational(u, v)
        else:
            raise TypeError
    def __truediv__(self, r ):
        """
        Переопределение операции /
        """
        return self.Div(r)
    

Для реализации перегрузки знаков операций - операторов необходимо знать имя соответствующей функции, например, интуитивно не очевидно, что для операции деления необходимо переопределить функцию с именем truediv. Все возможные функции, соответствующие операциям, находятся в модуле operator. Ознакомиться с именами функций можно, если импортировать этот модуль и получить список его атрибутов, используя функцию dir.

Пример сложного выражения, включающего все введенные операции:

def test7():
    r = (r1 - r2 - 2) * ((r3 + 1 + r4) * 2)) / (r5 - r6)
    print( 'r = ', r)

Вот результаты:

Следующим шагом является определение операций сравнения двух рациональных чисел. Дадим сразу определение знаков операций, не вводя эквивалентных методов:

def __eq__(self, r ):
        """
        Переопределение операции равно ==
        """
        if type(r) == classRational:
            return self.m == r.m and self.n == r.n
        elif type(r) == int:
            return self.m == r and self.n == 1
        else:
            raise TypeError
    
    def __ne__(self, r ):
        """
        Переопределение операции не равно !=
        """
        if type(r) == classRational:
            return self.m != r.m or self.n != r.n 
        elif type(r) == int:
            return self.m != r or self.n != 1
        else:
            raise TypeError
        
    
    def __ge__(self, r ):
        """
        Переопределение операции больше или равно >=
        """
        if type(r) == classRational:
            return self.m * r.n >= self.n * r.m  
        elif type(r) == int:
            return self.m >= r * self.n 
        else:
            raise TypeError
        
    
    def __le__(self, r ):
        """
        Переопределение операции меньше или равно <=
        """
        if type(r) == classRational:
            return self.m * r.n <= self.n * r.m  
        elif type(r) == int:
            return self.m <= r * self.n 
        else:
            raise TypeError
    
    def __gt__(self, r ):
        """
        Переопределение операции больше >
        """
        if type(r) == classRational:
            return self.m * r.n > self.n * r.m  
        elif type(r) == int:
            return self.m > r * self.n 
        else:
            raise TypeError
    
    def __lt__(self, r ):
        """
        Переопределение операции меньше <
        """
        if type(r) == classRational:
            return self.m * r.n < self.n * r.m  
        elif type(r) == int:
            return self.m < r * self.n 
        else:
            raise TypeError

Введение операций над рациональными числами позволяет клиенту класса строить выражения традиционным для чисел способом. Заметьте, что при вычислении значения выражения сохраняется приоритет, принятый для операций. Заметьте также, что в наших определениях как арифметических, так и логических операций первый операнд операции должен быть рациональным числом класса classRational, второй операнд может иметь тот же тип или быть целым числом типа int.

Вот пример арифметического и логического выражения с применением всех введенных операций:

def test8():
    r = (r1 + r2 * 3) / ((r3 - r4 + 2) * (r5 / r6)) -classRational(3,7)
    
    print ('r = ', r)
    if r == 7:
        print("Все арифметические операции выполняются корректно!")
    else:
        print("Не все арифметические операции выполняются корректно!")

    if (r1 == r2 and r3 <= r4 and
        (r4 >= r2 or r5 > r4) and
        r6 >= r1 and r6 > r5):
        print("Все логические операции выполняются корректно!")
    else: 
        print("Не все логические операции выполняются корректно!")

Вот результаты:

На этом закончим работу над классом Rational, оставляя в качестве упражнения добавление в класс операции возведения в целую степень и округления рационального числа, при котором в числителе и знаменателе остается не более k значащих цифр.

В заключение последний тест применим к рациональным числам, определенным в стандартной библиотеке Python:

def test9():
    from fractions import Fraction
    f1 = Fraction(3, 5);    print('f1 = ', f1)
    f2 = Fraction(3, 5);    print('f2 = ', f2)      
    f3 = Fraction(-3, 5);   print('f3 = ', f3)
    f4 = Fraction(0, 1);    print('f4 = ', f4)
    f5 = Fraction(3, 5);    print('f5 = ', f5)
    f6 = Fraction(13, 5);   print('f6 = ', f6)
    f = (f1 + f2 * 3) / ((f3 - f4 + 2) * (f5 / f6)) - Fraction(3,7)
    
    print ('f = ', f)
    print('type(f) = ', type(f))
    if f == 7:
        print("Все арифметические операции выполняются корректно!")
    else:
        print("Не все арифметические операции выполняются корректно!")

    if (f1 == f2 and f3 <= f4 and
        (f4 >= f2 or f5 > f4) and
        f6 >= f1 and f6 > f5):
        print("Все логические операции выполняются корректно!")
    else: 
        print("Не все логические операции выполняются корректно!")

Запустим одновременно оба теста - test8 и test9. Вот результаты, подтверждающие, что оба класса корректно работают:

Небольшая разница лишь в выводе рационального числа в случае, когда рациональное число эквивалентно целому. Рациональное число f в Python выводится как целое число. Я же предпочитаю выводить его в стандартной форме, подчеркивающей, что тип числа Rational, а не int.

Страницы:

На прошлой лекции мы начали проектирование класса, определяющего тип данных - рациональные числа. На этом этапе мы определили поля класса, конструктор класса и переопределили функцию __str__, наследуемую от родительского класса object. Построенный код позволяет клиентам класса создавать объекты проектируемого типа и выводить на печать методом print значения объектов в приемлемом формате.

Приведу построенный код, который будем пополнять в этой лекции:

class classRational():
    """
    Класс Rational.
	/// определяет новый тип данных - рациональные числа и основные
	/// операции над ними - сложение, умножение, вычитание и деление.
	/// Рациональное число задается парой целых чисел (m,n) и изображается,
	/// обычно в виде дроби m/n. Число m называется числителем, а 
	/// n - знаменателем. Для каждого рационального числа существует 
	/// множество его представлений, например 1/2, 2/4, 3/6, 6/12 -
	/// задают одно и тоже рациональное число. Среди всех представлений
	/// можно выделить то, в котором числитель и знаменатель взаимно 
	/// несократимы. Такой представитель будет храниться в полях класса.
	/// Операции над рациональными числами определяются естественным 
	/// для математиков образом. 
    """
    #поля класса
    # int m - числитель
    # int n - знаменатель
    def __init__(self,  m = 0, n = 1):
        """
        Конструктор класса. Создает рациональное число
	      m/n со взаимно несократимыми
	      числителем и знаменателем. 
          Параметры m, n - целые числа.
          Значения по умолчанию задают 
          рациональный ноль.
        """    
        #приведение знака
        if n < 0:
            m, n = -m, - n
        #приведение к несократимой дроби
        a, b = m, n
        if a < 0: 
            a = -a
        if a < b:
            a, b = b, a
        while b != 0:
            a, b = b, a % b
        d = a
        self.m = m // d
        self.n = n // d
        
    def __str__(self):
        """
        Переопределение встроенной функции __str__
        """
        return str(self.m) + "/" + str(self.n) 

Перейдем теперь к заданию методов класса, определяющих поведение объектов. Для рациональных чисел это операции, которые можно выполнять над этими числами. Начнем со сложения и определим метод Plus, позволяющий сложить два рациональных числа и получить в результате новое рациональное число. Вот его определение на Python:

    def Plus(self, r):
        """
        Сложение рациональных чисел
        """
        u = self.m * r.n + self.n * r.m
        v = self.n * r.n
        return classRational(u, v)
    

У метода два аргумента. Первым аргументом является текущий объект, имя которого фиксировано - self. Напомню, в других языках - С++, C#, Java - фиксированное имя текущего объекта - это this. Аргумент r задает слагаемое, прибавляемое к текущему объекту. Тип аргумента не задается в Pythonи успех операции обеспечивается лишь в том случае, если у фактического аргумента, передаваемого методу Plus будут атрибуты m и n, в противном случае возникнет исключительная ситуация, требующая обработки в процессе выполнения. Успех гарантируется, если второй аргумент действительно является рациональным числом.

Метод Plus позволяет выполнять операции над рациональными числами в объектном стиле. Синтаксически значительно эффективнее перегрузить операцию "+". Вместо присваивания в форме:

$$r5 = r1.Plus(r2)$$

рекомендуется использовать форму, привычную при работе с числами:

$$r6 = r3 + r4.$$

Язык Pythonпозволяет выполнять перегрузку всех знаков операций, применяемых при работе с числами. Но поскольку перегрузки в языке Pythonнет, то перегрузка знаков операций моделируется переопределением методов, соответствующих каждому знаку операции. Покажем, как это делается, на примере операции "+":

  def __add__(self, r ):
        """
        Переопределение операции +
        """
        return self.Plus(r)
    

В определении использован созданный метод Plus. Клиент класса может по желанию использовать любую форму выполнения операции сложения. Вот пример:

def test5():    
    global r5, r6
    r5 = r1.Plus(r2)
    r6 = r3 + r4
    print("r5 = ", r5)
    print("r6 = ", r6)

Вот результаты работы двух тестов - test1, test5, создающих глобальные переменные: r1 - r6:

О перегрузке методов

Рассматривая метод сложения, нам, конечно, хотелось бы иметь вариант, когда к рациональному числу прибавляется целое число типа int. Обычное решение этой проблемы состоит в том, чтобы для этого варианта создать перегруженный метод с тем же именем Plus, но с аргументом другого типа - int, а не classRational. Однако, как мы знаем, перегрузки методов класса в Python нет, - проблему нужно решать другим способом. Такой способ есть. Он состоит в анализе типа аргумента. Конструкция if - elif, встроенная в метод, позволяет создать нужные варианты перегрузки. Воспользуемся этим способом и создадим новый вариант метода Plus. Старый метод сохраним, переименовав его в метод Plus_old. Вот новая версия метода Plus, позволяющая к рациональному числу прибавлять как рациональное число, так и целое число:

   def Plus(self, r):
        """
        Определение метода сложения рациональных чисел
        Аргумент r может быть рациональным или целым числом
        Для других типов аргумента возбуждается исключение
        """
        if type(r) == classRational:
            u = self.m * r.n + self.n * r.m
            v = self.n * r.n
            return classRational(u, v)
        elif type(r) == int:            
            u = self.m + self.n * r 
            v = self.n 
            return classRational(u, v)
        else:
     raise TypeError

Как видите, выполняется проверка типа аргумента. Для каждого возможного типа аргумента выполняются требуемые действия. Для аргумента недопустимого типа метод raise возбуждает исключение стандартного типа TypeError, уведомляющее о недопустимом типе. Исключение может быть перехвачено в программе и обработано соответствующим образом. Сейчас я не буду вдаваться в подробности работы с исключениями. Им будет посвящена отдельная лекция.

Заметьте, изменение метода Plus не отражается на определении операции "+". В нашем следующем примере будут строиться выражения, в которых операндами выступают как рациональные, так и целые числа:

def test6():
    global r5, r6
    r5 = r1 + r2 + r3
    r6 = r1 + 2 + r4
    print("r5 = ", r5)
    print("r6 = ", r6)
    #r7 = r1 + 5.5  

Вот результаты работы этого теста:

Заметьте, последний оператор присваивания в этом тесте закомментирован. Если его раскомментировать, то возникнет исключение. Поскольку перехват исключения не предусмотрен, то будет работать обработчик стандартного исключения, который выведет соответствующее сообщение об ошибке. Приведу текст сообщения об ошибке:

Мы подробно рассмотрели, как строить метод Plus, выполняющий сложение, как перегрузить соответствующую операцию "+". Приведем теперь уже без комментариев реализацию других арифметических операций - вычитания, умножения, деления:

    
def Minus(self, r):
        """
        Определение метода вычитания рациональных чисел
        Аргумент может быть рациональным или целым числом
        Для других типов аргумента возбуждается исключение
        """
        if type(r) == classRational:
            u = self.m * r.n - self.n * r.m
            v = self.n * r.n
            return classRational(u, v)
        elif type(r) == int:            
            u = self.m - self.n * r
            v = self.n 
            return classRational(u, v)
        else:
            raise TypeError
    def __sub__(self, r ):
        """
        Переопределение операции -
        """
        return self.Minus(r)


    def Mult(self, r):
        """
        Определение метода умножения рациональных чисел
        Аргумент может быть рациональным или целым числом
        Для других типов аргумента возбуждается исключение
        """
        if type(r) == classRational:
            u = self.m * r.m 
            v = self.n * r.n
            return classRational(u, v)
        elif type(r) == int:            
            u = self.m * r
            v = self.n 
            return classRational(u, v)
        else:
            raise TypeError
    def __mul__(self, r ):
        """
        Переопределение операции *
        """
        return self.Mult(r)



    def Div(self, r):
        """
        Определение метода деления рациональных чисел
        Аргумент может быть рациональным или целым числом
        Для других типов аргумента возбуждается исключение
        """
        if type(r) == classRational:
            u = self.m * r.n 
            v = self.n * r.m
            return classRational(u, v)
        elif type(r) == int:            
            u = self.m 
            v = self.n * r
            return classRational(u, v)
        else:
            raise TypeError
    def __truediv__(self, r ):
        """
        Переопределение операции /
        """
        return self.Div(r)
    

Для реализации перегрузки знаков операций - операторов необходимо знать имя соответствующей функции, например, интуитивно не очевидно, что для операции деления необходимо переопределить функцию с именем truediv. Все возможные функции, соответствующие операциям, находятся в модуле operator. Ознакомиться с именами функций можно, если импортировать этот модуль и получить список его атрибутов, используя функцию dir.

Пример сложного выражения, включающего все введенные операции:

def test7():
    r = (r1 - r2 - 2) * ((r3 + 1 + r4) * 2)) / (r5 - r6)
    print( 'r = ', r)

Вот результаты:

Следующим шагом является определение операций сравнения двух рациональных чисел. Дадим сразу определение знаков операций, не вводя эквивалентных методов:

def __eq__(self, r ):
        """
        Переопределение операции равно ==
        """
        if type(r) == classRational:
            return self.m == r.m and self.n == r.n
        elif type(r) == int:
            return self.m == r and self.n == 1
        else:
            raise TypeError
    
    def __ne__(self, r ):
        """
        Переопределение операции не равно !=
        """
        if type(r) == classRational:
            return self.m != r.m or self.n != r.n 
        elif type(r) == int:
            return self.m != r or self.n != 1
        else:
            raise TypeError
        
    
    def __ge__(self, r ):
        """
        Переопределение операции больше или равно >=
        """
        if type(r) == classRational:
            return self.m * r.n >= self.n * r.m  
        elif type(r) == int:
            return self.m >= r * self.n 
        else:
            raise TypeError
        
    
    def __le__(self, r ):
        """
        Переопределение операции меньше или равно <=
        """
        if type(r) == classRational:
            return self.m * r.n <= self.n * r.m  
        elif type(r) == int:
            return self.m <= r * self.n 
        else:
            raise TypeError
    
    def __gt__(self, r ):
        """
        Переопределение операции больше >
        """
        if type(r) == classRational:
            return self.m * r.n > self.n * r.m  
        elif type(r) == int:
            return self.m > r * self.n 
        else:
            raise TypeError
    
    def __lt__(self, r ):
        """
        Переопределение операции меньше <
        """
        if type(r) == classRational:
            return self.m * r.n < self.n * r.m  
        elif type(r) == int:
            return self.m < r * self.n 
        else:
            raise TypeError

Введение операций над рациональными числами позволяет клиенту класса строить выражения традиционным для чисел способом. Заметьте, что при вычислении значения выражения сохраняется приоритет, принятый для операций. Заметьте также, что в наших определениях как арифметических, так и логических операций первый операнд операции должен быть рациональным числом класса classRational, второй операнд может иметь тот же тип или быть целым числом типа int.

Вот пример арифметического и логического выражения с применением всех введенных операций:

def test8():
    r = (r1 + r2 * 3) / ((r3 - r4 + 2) * (r5 / r6)) -classRational(3,7)
    
    print ('r = ', r)
    if r == 7:
        print("Все арифметические операции выполняются корректно!")
    else:
        print("Не все арифметические операции выполняются корректно!")

    if (r1 == r2 and r3 <= r4 and
        (r4 >= r2 or r5 > r4) and
        r6 >= r1 and r6 > r5):
        print("Все логические операции выполняются корректно!")
    else: 
        print("Не все логические операции выполняются корректно!")

Вот результаты:

На этом закончим работу над классом Rational, оставляя в качестве упражнения добавление в класс операции возведения в целую степень и округления рационального числа, при котором в числителе и знаменателе остается не более k значащих цифр.

В заключение последний тест применим к рациональным числам, определенным в стандартной библиотеке Python:

def test9():
    from fractions import Fraction
    f1 = Fraction(3, 5);    print('f1 = ', f1)
    f2 = Fraction(3, 5);    print('f2 = ', f2)      
    f3 = Fraction(-3, 5);   print('f3 = ', f3)
    f4 = Fraction(0, 1);    print('f4 = ', f4)
    f5 = Fraction(3, 5);    print('f5 = ', f5)
    f6 = Fraction(13, 5);   print('f6 = ', f6)
    f = (f1 + f2 * 3) / ((f3 - f4 + 2) * (f5 / f6)) - Fraction(3,7)
    
    print ('f = ', f)
    print('type(f) = ', type(f))
    if f == 7:
        print("Все арифметические операции выполняются корректно!")
    else:
        print("Не все арифметические операции выполняются корректно!")

    if (f1 == f2 and f3 <= f4 and
        (f4 >= f2 or f5 > f4) and
        f6 >= f1 and f6 > f5):
        print("Все логические операции выполняются корректно!")
    else: 
        print("Не все логические операции выполняются корректно!")

Запустим одновременно оба теста - test8 и test9. Вот результаты, подтверждающие, что оба класса корректно работают:

Небольшая разница лишь в выводе рационального числа в случае, когда рациональное число эквивалентно целому. Рациональное число f в Python выводится как целое число. Я же предпочитаю выводить его в стандартной форме, подчеркивающей, что тип числа Rational, а не int.

Вернуться к учебному плану