Полиморфизм

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

Например, два разных класса содержат метод total, однако инструкции каждого предусматривают совершенно разные операции. Так в классе T1 – это прибавление 10 к аргументу, в T2 – подсчет длины строки символов. В зависимости от того, к объекту какого класса применяется метод total, выполняются те или иные инструкции.

class T1:
     n=10
     def total(self, N):
          self.total = int(self.n) + int(N)
 
class T2:
     def total(self,s):
          self.total = len(str(s))
 
t1 = T1()
t2 = T2()
t1.total(45)
t2.total(45)
print(t1.total) # Вывод: 55
print(t2.total) # Вывод: 2

В предыдущем уроке мы уже наблюдали полиморфизм между классами, связанными наследованием. У каждого может быть свой метод __init__() или square() или какой-нибудь другой. Какой именно из методов square() вызывается, и что он делает, зависит от принадлежности объекта к тому или иному классу.

Однако классы не обязательно должны быть связанны наследованием. Полиморфизм как один из ключевых элементов ООП существует независимо от наследования. Классы могут быть не родственными, но иметь одинаковые методы, как в примере выше.

Полиморфизм дает возможность реализовывать так называемые единые интерфейсы для объектов различных классов. Например, разные классы могут предусматривать различный способ вывода той или иной информации объектов. Однако одинаковое название метода вывода позволит не запутать программу, сделать код более ясным.

В Python среди прочего полиморфизм находит отражение в методах перегрузки операторов. Два из них мы уже рассмотрели. Это __init__() и __del__(), которые вызываются при создании объекта и его удалении. Полиморфизм у методов перегрузки операторов проявляется в том, что независимо от типа объекта, его участие в определенной операции, вызывает метод с конкретным именем. В случае __init()__ операцией является создание объекта.

Рассмотрим пример полиморфизма на еще одном методе, который перегружает функцию print().

Если вы создадите объект собственного класса, а потом попробуете вывести его на экран, то получите информацию о классе объекта и его адрес в памяти. Такое поведение функции print() по-умолчанию по отношению к пользовательским классам запрограммировано на самом верхнем уровне иерархии, где-то в суперклассе, от которого неявно наследуются все остальные.

>>> class A:
...     def __init__(self, v1, v2):
...             self.field1 = v1
...             self.field2 = v2
... 
>>> a = A(3, 4)
>>> print(a)
<__main__.A object at 0x7f840c8acfd0>

Если же мы хотим, чтобы, когда объект передается функции print(), выводилась какая-нибудь другая более полезная информация, то в класс надо добавить специальный метод __str__(). Этот метод должен обязательно возвращать строку, которую будет выводить функция print():

class A:
    def __init__(self, v1, v2):
        self.field1 = v1
        self.field2 = v2
    def __str__(self):
        return str(self.field1) + " " + str(self.field2)
 
a = A(3, 4)
print(a)

Вывод:

3 4

Какую именно строку возвращает метод __str__(), дело десятое. Он вполне может строить квадратик из символов:

class Rectangle:
    def __init__(self, width, height, sign):
        self.w = int(width)
        self.h = int(height)
        self.s = str(sign)
    def __str__(self):
        rect = []
        for i in range(self.h): # количество строк
            rect.append(self.s * self.w) # знак повторяется w раз
        rect = '\n'.join(rect) # превращаем список в строку
        return rect
 
b = Rectangle(10, 3, '*')
print(b)

Вывод:

**********
**********
**********

Практическая работа. Метод перегрузки оператора сложения

В качестве практической работы попробуйте самостоятельно перегрузить оператор сложения. Для его перегрузки используется метод __add__(). Он вызывается, когда объекты класса, имеющего данный метод, фигурируют в операции сложения, причем с левой стороны. Это значит, что в выражении a + b у объекта a должен быть метод __add__(). Объект b может быть чем угодно, но чаще всего он бывает объектом того же класса. Объект b будет автоматически передаваться в метод __add__() в качестве второго аргумента (первый – self).

Отметим, в Python также есть правосторонний метод перегрузки сложения - __radd__().

Согласно полиморфизму ООП, возвращать метод __add__() может что угодно. Может вообще ничего не возвращать, а "молча" вносить изменения в какие-то уже существующие объекты. Допустим, в вашей программе метод перегрузки сложения будет возвращать новый объект того же класса.

Курс с примерами решений практических работ и всеми уроками: android-приложение, pdf-версия.

Комментарии

class Unit(object):
    '''Unit Class'''
    def __init__(self, hp, damage):
        self.hp = hp
        self.damage = damage
 
    def __str__(self):
        return f'Unit. HP: {self.hp}, damage: {self.damage}'
 
    def __add__(self, other):
        new_hp = int((self.hp + other.hp) * 0.5)
        new_damage = int(((self.damage + other.damage) * 0.7))
        return Unit(new_hp, new_damage)
 
 
def main():
    unit_01 = Unit(50, 10)
    unit_02 = Unit(75, 15)
    new_unit = unit_01 + unit_02
 
    print(new_unit)
 
 
if __name__ == '__main__':
    main()

Иногда ввообще не ясно что делать в практическом задании..у обьекта а не может быть метода " __add__ " вызваного напрямую.. как и в предидущем задании смысла нет..но все же:
class A:
    def num(self, x):
        self.a = x
 
    def __add__(self, y):
        self.b = y
        self.s = str(self.a)+str(self.b)
 
    def add(self, y):
        A.__add__(self, y)
        return self.s 
 
q = A()

class Birds():
    wings = 2
    beak = 1
    tail = 1
    legs = 2
 
    def __add__(self, b):
        return Birds()
 
    def __str__(self):
        return "Chicken has {} wings, {} legs, {} beak, {} tail.".format(
            chicken.wings, chicken.legs, chicken.beak, chicken.tail)
 
hen = Birds()
rooster = Birds()
 
chicken = hen + rooster
print(chicken)

Ответ на от Alisa

class Birds():
	wings = 2
	beak = 1
	tail = 1
	legs = 2
 
	def __add__(self, b):
		self.wings += b.wings
		self.beak += b.beak
		self.tail += b.tail
		self.legs += b.legs
		return self
 
	def __str__(self):
		return "Chicken has {} wings, {} legs, {} beak, {} tail.".format(
			self.wings, self.legs, self.beak, self.tail)
 
hen = Birds()
rooster = Birds()
 
chicken = hen + rooster
print(chicken)
Вывод:
Chicken has 4 wings, 4 legs, 2 beak, 2 tail.

Домашняя работа.
import random
class Animals():
    def __init__(self, name):
        self.name = name
    def __add__(self, b):
        new_name = self.name +"о"+ b.name
        return Animals(new_name)
 
animals=["жираф", "динозавр", "крот", "лев", "хомяк", "заяц", "тигр"]
animals_cl=[]
for a in animals:
    animals_cl.append(Animals(a))
 
new_animal = random.choice(animals_cl) + random.choice(animals_cl)
print("Новое животное зовут", new_animal.name)

Ответ на от AnnaKo

Я бы немного поправил бы в конце код
animals_cl_rdnt = animals_cl[:]
first_part = random.choice(animals_cl_rdnt)
del animals_cl_rdnt[animals_cl_rdnt.index(first_part)]
second_part = random.choice(animals_cl_rdnt)
new_animal = first_part + second_part
print("Новое животное зовут", new_animal.name)

class hamster:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __add__(self, other_hamster):
        return hamster(self.y, other_hamster.x)
    def __str__(self):
        return str(self.x) + " " + str(self.y)
p1 = hamster(3, 5)
p2 = hamster(4, 9)
print(p1 + p2)

class A:
    def __add__(self, b):
        a = int(6)
        return a * int(b)
 
    def Somebody(self, h, d):
        self.h = h
        self.d = d
        return h + d
 
p = A()
c = A()
print (p.__add__(4) + c.Somebody(3, 7))
Правильно ли я понял задание ?

 

class Thing:
    def __init__(self, material):
        self.material = material

    def __add__(self, other):
        if self.material == other.material:
            return f"The room's furniture is {self.material}"
        else:
            return "The room's furniture is made from different materials"


class Table(Thing):
    def somethig(self):
        pass


class Chair(Thing):
    def anything(self):
        pass


table = Table(str(input("Enter material of table...\n")))
chair = Chair(str(input("Enter material of chair...\n")))

print(table + chair)

 

class A:
    def __init__(self, a, b):
        self.a = a
        self.b = b
    def __add__(self, other):
        self.other = self.b
    def __str__(self):
        return str(self.a + self.b)
p1 = A(33,31)
print(p1)

Только я не понял зачем __add__, если и без него работает :(

class Polimorf:
	def __init__(self,x):
		self.x=int(x)
 
	def __add__(self,x):
		add=self.x+self.x
		return str(add) + ' - это сумма двух объектов типа Polimorf.'
 
a=Polimorf(5)
b=Polimorf(2)
print(a+b)
Вопрос: add является объектом класса Polimorf?

Ответ на от Xp

Формально - да. С точки зрения какой-либо логики, следует что-то с чем-то складывать. Например, метод __add__() принимает не только self, но и другой объект того же типа. Из обоих извлекает поле x. Складывает. Создает новый объект с суммарным полем x. Возвращает его. 

Вызов метода: a + b.

Ответ на от plustilino

Тогда вот так (ответьте пожалуйста - выполнено ли здесь условие задачи?)
class Polimorf:
	def __init__(self,x):
		self.x=int(x)
 
	def __str__(self):
		return str(self.x)
 
	def __add__(self,x):
		y=self.x+self.x
		z=Polimorf(y)
		return z
 
a=Polimorf(5)
b=Polimorf(2)
print('a=',a)
print('b=',b)
print(a+b)
И ещё, я заметил ошибку (она есть и в других вариантах). Я думал, что складывая объект а и объект b будут взяты их значения 5 и 2 соответственно. А оказывается складываются только а. Т. о. ответ получается 10, когда правильным должен быть 7. Как решить эту проблему?

from random import random
 
maleNames = ["Abram", "August", "Avdey", "Avtandil", "Adam", "Adis", "Adolf", "Adrian", "Azariah", "Alexander",
"Alexey", "Albert", "Alfred", "Amadeus", "Bernard", "Bogdan", "Bogolyub", "Boleslav", "Boniface", "Boris",
"Borislav", "Boyan", "Vasiliy", "Velizar", "Benedict", "Benjamin", "Victor", "Wilen", "Willy", "William",
"Vissarion", "Vitali", "Vytautas", "Witold", "Vladimir", "Vladislav", "Harry", "Gaston", "Gennady", "Henry",
"Georgy", "Herald", "Gerasim", "Hermann", "Gleb", "Gordey", "Gordon", "Gradimir", "Gregory", "Denis", "Jordan",
"Dmitry", "Donald", "Donat", "Donatas", "Eugraph", "Evdokim", "Eustace", "Egor", "Elizar", "Elisha", "Yemelyan",
"Ermolai", "Erofei", "Efim", "Jean", "Zhdan", "Georges", "Zahar", "Zacharias", "Sigmund", "Zinovy", "Ivan", "Igor",
"Ishmael", "Israel", "Ilian", "Hilarion", "Ilya", "Innocent", "And he", "Joseph", "Irakli", "Jiri", "Casimir",
"Karen", "Charles", "Kim", "Kirill", "Claudius", "Clement", "Claude", "Kondrat", "Concord", "Lazarus", "Levan",
"Leonard", "Leonid", "Leonti", "Leopold", "Luke", "Lubomir", "Ludwig", "Maxim", "Maximilian", "Manuel", "Marat",
"Marian", "Mark", "Matthew", "Merab", "Milan", "Miron", "Miroslav", "Michael", "Michlov", "Modest", "Moses",
"Nazar", "Nazariy", "Nathan", "Nahum", "Nikita", "Nikifor", "Nicholas", "Nikon", "Neeson", "Oleg", "Oles",
"Onesimus", "Orest", "Osip", "Oscar", "Paul", "Paramon", "Peter", "Plato", "Porphyry", "Prohor", "Ravil",
"Radomir", "Rais", "Raymond", "Ratmir", "Raphael", "Rafiq", "Rasheed", "Rem", "Renold", "Rifat", "Richard",
"Robert", "Rodion", "Roland", "Novel", "Rostislav", "Ruben", "Rudolf", "Ruslan", "Samson", "Svyatoslav",
"Sevastian", "Severin", "Semen", "seraph", "Sergei", "Socrates", "Solomon", "Spartacus", "Stakrat",
"Stanislas", "Stepan", "Stephen", "Stojan", "Thais", "talik", "Tamaz", "Taras", "Telman", "Theodore",
"Terence", "Tibor", "Tigran", "tigers", "Timothy", "Timur", "Titus", "Tikhon", "Trifon", "Trofim", "Thaddeus",
"Fedor", "Felix", "Theodosius", "Fidel", "Philemon", "Philip", "Florentius", "Thomas", "Francis", "Frederick",
"Jacob", "Yang", "Jaromir", "Yaroslav", "Jason"]
 
femaleNames = ["Aurora", "Agatha", "Agnes", "Agnes", "Adele", "Aziz", "Aida", "Alan", "Alevtina", "Alexandra", "Aliko", "Alina",
"Alice", "Alla", "alberta", "Albina", "Alzhbeta", "Amelia", "Amin", "Anastasia", "Angelina", "Angela", "Anisa",
"Anita", "Anna", "Antonina", "Anfisa", "Anel", "Ariadne", "Arina", "Archelaus", "Aster", "Aurelia", "Beatrice",
"Bella", "Bereslava", "Bertha", "Birgit", "Bogdan", "Bozena", "Borislav", "Bronislaw", "Valentine", "Valeria",
"Wanda", "Varvara", "Vasilisa", "Venus", "Vera", "Veronica", "Veselin", "Vesta", "Victoria", "Vilora", "Violet",
"Virginia", "Vitalina", "Vladislav", "Galina", "Gayane", "Helena", "Gella", "Henrietta", "Dahlia", "Gera",
"Gertrude", "Glafira", "Gloria", "Grazyna", "Greta", "Guzel", "Dinah", "Dana", "Daniela", "Danuta", "Darina",
"Darya", "Deborah", "Gemma", "Julia", "Juliet", "Diana", "Dina", "Dinara", "Diodorus", "Dionysius", "share",
"Dominica", "Eve", "Eugene", "Evdokia", "Ekaterina", "Elena", "Elizabeth", "Jeanne", "Zara", "Zemfira",
"Zinaida", "Zlata", "Zoe", "Yvette", "Ivona", "Isabel", "Isolde", "Ilze", "Inara", "Inga", "Inessa", "Inna",
"John", "Iolanthe", "Iraida", "Irina", "Olga", "Sabina", "Santa", "Sarah", "Svetlana", "Severin", "Seraphim",
"Silvia", "Sima", "Simone", "Snezana", "Sofia", "Stanislaus", "Stella", "Stephanie", "Susanna", "Tahir",
"Taisiya", "Tala", "Tamara", "Tatyana", "Theresa", "Tomila", "Faiza", "Faina", "Fania", "Faya", "Felice",
"Flora", "Françoise", "Frieda", "Edda", "Edita", "Eleanor", "Elina", "Ella", "Hellas", "Eloise", "Elvira",
"Elga", "Elsa", "Elmira", "Emilia", "Emma", "Erika", "Esmeralda", "Jozef", "Julia", "Yuna", "Juno", "Justin",
"Jadwiga", "Yana", "Yanit", "Janka", "Yaroslav"]
 
surnames = ["Fadeev", "Molchanov Ignatov", "Litvinov",
"Ershov", "Ushakov", "Dementiev", "Ryabov",
"Mukhin", "Kalashnikov", "Leontiev", "Lobanov",
"Kuzin", "Korneev", "Evdokimov", "Borodin",
"Platonov", "Nekrasov", "Balashov", "Bobrov",
"Zhdanov", "Blinov", "Ignatiev", "short",
"Ants", "Kryukov", "Belyakov", "Bogomolov",
"Drozdov", "Lavrov", "Zuev", "Petukhov",
"Larin", "Nikulin", "Serov", "Terentyev",
"Zotov", "Ustinov", "Fokin", "Samoilov",
"Konstantinov", "sugar", "Shishkin", "Samsonov",
"Cherkasov", "Chistyakov", "Nosov", "Spiridonov",
"Karasev", "Avdeev", "Vorontsov", "Zverev",
"Vladimirov", "Seleznev", "Nechayev", "Kudryashov",
"Sedov", "Firsov", "Andrianov", "Panin", "Golovin",
"Terekhov", "Ulyanov", "Shestakov", "Ageev",
"Nikonov", "Selivanov", "Bazhenov", "Gordeev",
"Kozhevnikov", "Pakhomov", "Zimin", "Kostin", "Shirokov",
"Filimonov", "Larionov", "Ovsyannikov", "Sazonov",
"Suvorov", "Nefedov", "Kornilov", "Lyubimov",
"Lviv", "Gorbachev", "Kopylov", "Lukin",
"Tokarev", "Kuleshov", "Shilov", "Bolshakov",
"Pankratov", "Rodin", "Shapovalov", "Basil",
"Bocharov", "Nicholas Markin", "Gorelov",
"Agafonov", "Berezin", "Ermolaev", "Zubkov",
"Kupriyanov", "Trifonov", "Maslennikov", "round",
"Tretyakov", "Kolosov", "Rozhkov", "Artamonov",
"Shmelev", "Laptev", "Lapshin", "Fedoseyev",
"Zinoviev", "Zorin", "Utkin", "Stolyarov",
"Teeth", "Tkachev", "Dorofeev", "Antipov",
"Zavyalov", "Sviridov", "Zolotarev", "Fists",
"Meshcheryakov", "Makeev", "Deacons", "walking",
"Petrovsky", "Bondarev", "Pozdnyakov",
"Panfilov", "Kochetkov", "Sukhanov", "Ryzhov",
"Starostin", "Kalmykia", "Kolesov", "gold",
"Kravtsov", "Subbotin", "Shubin", "Shchukin", "Losev",
"Vinokourov", "Lapin", "Parfenov", "Isakov",
"Golovanov", "Korovin", "Rozanov", "Artemov",
"Kozyrev", "Rusakov", "Aleshin", "hooks",
"Bulgakov", "Koshelev", "Sychev", "Sinitsyn",
"Black", "horns", "Kononov", "Lavrentiev",
"Evseev", "Pimenov", "Panteleev", "hot",
"Anikin", "Lopatin", "Rudakov", "Odintsov",
"Serebryakov", "Pankow", "Degtyarev", "nuts",
"Tsarev", "Shuvalov", "Kondrashov", "Goryunov",
"Dubrovin", "Golikov", "Kurochkin", "Latvians",
"Sevastyanov", "Vavilov", "Yerofeyev", "Salnikov",
"Klyuyev", "socks", "Ozerov", "rings",
"Commissioners", "Merkulov", "Kireev",
"Hamsters", "Bulatov", "Ananiev", "Burov",
"Shaposhnikov", "Druzhinin", "Ostrovsky",
"Shevelev", "Dolgov", "Suslov", "Shevtsov",
"Shepherds", "Rubtsov", "Bychkov", "Glebov",
"Ilyinsky", "Assumption", "Dyakov", "Kochetov",
"Wisniewski", "Vysotsky", "Glukhov", "Oaks",
"Bessonov", "Sitnikov", "Astafjevs", "Sacks",
"Balls", "Yashin", "Kozlovsky", "Tumanov", "Basov",
"Korchagin", "Boldyrev", "Oleynikov", "Chumakov",
"Fomichev", "Gubanov", "Dubinin", "Shulgin",
"Kasatkin", "Pirogov", "Semin", "Troshin", "peas",
"Starikov", "Goldfinches", "Fetisov", "Kolpakov",
"Chesnokov", "Zykov", "Vereshchagin", "Minaev", "Rudnev",
"Trinity", "Okulov", "Shiryaev", "Malinin",
"Cherepanov", "Izmailov", "Alekhine", "Zelenin", "Kasyanov",
"Pugachev", "Pavlovsky", "Chizhov", "Kondrashov",
"Voronkov", "Kapustin", "Sotnikov", "Demyanov", "Kosarev",
"Belikov", "Sukharev", "Belkin", "Bespalov",
"Kulagin", "Savitsky", "Zharov", "chromium", "Eremeev",
"Kartashov", "Astakhov", "Rusanov", "Sukhov",
"Veshnyakov", "Voloshin", "Kozin", "Khudyakov", "Jilin",
"Malakhov", "Sizov", "Yezhov", "Tolkachev", "Anokhin",
"Vdovin", "Grandmother", "Usov", "Lykov", "throat",
"Korshunov", "Markelov", "Postnikov", "Black", "Dorokhov",
"Sveshnikov", "Gushchin", "Kalugin", "Blokhin",
"Surkov", "Kochergin", "Greeks", "Kazantsev", "Sweden",
"Ermilov", "Paramonov", "Agapov", "Minin",
"Kornev", "Chernyaev", "Gurov", "Yermolov", "Somov",
"Dobrynin", "Barsukov", "Glushkov Chebotarev",
"Moskvin", "Uvarov", "Bezrukov", "Muratov",
"Crayfish", "Snegirev", "smooth", "Zlobin",
"Morgunov", "Polikarpov", "Ryabinin", "perch",
"Kukushkin", "Kalachev", "mushrooms", "Elizarov",
"Zvyagintsev", "Korol'kov", "Fedosov"
]
 
genderDict = {0: 'female', 1: 'male'}
 
class Person:
    def __init__(self, gender='<auto>', name='<auto>', surname='<auto>', skill=1):
 
        self.gender = gender
        if self.gender == '<auto>':
            self.gender = genderDict[int(random()*2)]
 
        self.name = name
        if self.name == '<auto>' and self.gender == "male":
            self.name = maleNames[int(random()*len(maleNames))]
        elif self.name == '<auto>' and self.gender == "female":
            self.name = femaleNames[int(random() * len(femaleNames))]
 
        self.surname = surname
        if self.surname == '<auto>':
            self.surname = surnames[int(random() * len(surnames))]
        if self.gender == 'female':
            self.surname += "a"
 
        self.skill = skill
 
    def __str__(self):
        return f"Is {self.gender}, {self.name} {self.surname} with skill={self.skill}"
 
    def __add__(self, person): #x + 1
        if self.getGender() != person.getGender():
            newperson = Person(surname=self.getSurname())
            return newperson
        else:
            return "Ata-ta!"
 
    def __iadd__(self, person): # x += 1
        print(self, " & ", person)
 
    def __radd__(self, person): #1 + x
        self.__add__(person, self)
 
 
    def getGender(self):
        return self.gender
 
    def getSurname(self):
        return self.surname
 
 
if __name__ == '__main__':
    a1 = Person()
    a2 = Person()
    print(a1)
    print(a2)
    a3 = a1 + a2
    print(a3)
 
    #a2 += a1

Ответ на от Xp

Если ты внимательно будешь читать, то увидишь следующее предложение <>: <<Если вы создадите объект собственного класса, а потом попробуете вывести его на экран, то получите информацию о классе объекта и его адрес в памяти.>>

Но что такое объект...это по сути сосуд, контейнер (переменная/класс) со своим "уникальным" названием, который наследует свойства родительского класса, и когда ты пытаешься вывести обычным print-ом сам объект, это эквивалентно как если бы ты попросил интерпретатор <<Выведи мне объект.>>, что собственно он и делает, он показывает тебе <<ящик Пандоры>> (выводит класс объекта и его адрес в памяти,или так называемые <<указатели>>) но не как его данные в привычном для человека виде.

Поэтому будет логичнее попросить интерпретатор, чтобы он вывел нужные тебе данные объектов созданных на основе конструктора классов __init__(), что и позволяет сделать метод __str__() (о чём я к сожалению не знал когда изучал наследование, ибо хотел написать базу данных при помощи отдельной функции....плак....плак...я дурак).

print(end='\n'*3)
 
 
class Add:
    def __init__(self, argument):
        self.argument = argument
 
    def __add__(self, other):
        self.sum= other + self.argument
        return self.sum  # Сохраним результат в данную функцию.
 
    def __str__(self):
    	return(str(self.sum)) # Превратим результат из числового
    	                      # в строковый и сохраним в данной функции,
    	                      # которая автоматически вызывается когда
    	                      # к объекту применяется метод print().
z = Add(12)
x = Add(2)
z.__add__(x) # Срабатывает, потому что  мы вызвали данный метод
	     # и применили к объекту z.
 
print(z) # Здесь у нас вызывается z.__str__(self), в котором
		 # сохранён рузультат нашей суммы.
 
 
print(end='\n'*3)