[МУЗЫКА] [МУЗЫКА] [МУЗЫКА] Итак, мы остановились на выводе комплексных чисел. На самом деле, когда вы вызываете функцию print для любого объекта, какой бы он ни был, фактически у вас вызывается нечто, подобное функции str от этого объекта. То есть у нас перед тем как напечататься, любой объект превращается в строку. Но на самом деле функция str, в свою очередь, является всего лишь оберткой над стандартным методом, который определен у всех стандартных объектов и который мы можем определить для своего объекта. Этот метод называется подчеркивание, подчеркивание str (__str). То есть на самом деле, когда вы вызовете str от чего угодно, то произойдет всего лишь вызов метода __str__ для этого объекта. Никаких параметров он не принимает, то есть единственный его параметр — это self, сам объект, это обязательно, и нам нужно вернуть строковое представление объекта. В нашем случае как должно печататься наше комплексное число? Мы должны превратить в число его действительную часть, затем сделать значок «плюс», как константная строка, затем строковое представление его мнимой части и буква i, тоже как строковая константа. Такой метод у нас сделает нам то, что нужно. Здесь я забыл self, у нас это поля, по сути, нашего объекта. Не забываем, вот оно подчеркнуло, и сразу удалось эту ошибку обнаружить. Все, давайте теперь попробуем, как будет выглядеть вывод нашего объекта, если мы просто попытаемся вызвать функцию print. Вот такой вывод, достаточно правильный. Ну, как мы написали, так оно и выводит. Очень хорошо. При этом, что мы можем сделать еще? Мы можем попробовать вызвать функцию str и посмотреть, действительно ли наша функция str научилась работать. А для того чтобы было веселее, давайте напечатаем два объекта. Вот теперь функция str стандартная, научилась давать строковое представление нашего объекта. Если бы у нас не было объектно-ориентированного программирования, то нам пришлось бы лезть куда-то в стандартную библиотеку и там переписывать функцию str, которую нам должны были дать, этот исходный код, каким-то образом, что, вообще говоря, не всегда возможно, чтобы научить ее работать с нашим типом объекта. Но когда у нас str — всего лишь обертка над методом класса, все это делается легко и непринужденно, и ни в какие стандартные библиотеки мы не залезаем. Давайте посмотрим, какие еще методы можно определить. В Python очень много стандартных разных методов. Например, плюс определен для многих объектов — для строк, для чисел, и мог бы быть определен для каких-нибудь кортежей или списков, если бы это имело какой-то смысл. Часть объектов, которые вы будете использовать, имеет логичную операцию «плюс». Для комплексных чисел, как для математических объектов, операция «плюс» вообще однозначно определена, и математики такой записью пользуются, и поэтому мы можем ее сделать. Давайте сначала сделаем, потом я расскажу, когда не стоит так поступать. Итак, метод, опять же, с этими подчеркиваниями, add — «добавить». Что он должен принимать? Он должен принимать self — себя, как и все методы, и еще один параметр — other. То есть что это такое? Это наш объект, и второй объект, с которым будем складывать того же самого типа комплексное число. Результатом должен быть новый объект, то есть мы не меняем self, а возвращаем новый объект. Почему? Потому что когда вы пишете, например, a = 2 + 3, вы ожидаете, что в переменной a создастся новое число, равное 5, а не двойка превратится в пятерку. Вот и здесь такая же логика, то есть мы не меняем слагаемые, а создаем новый объект с результатом. Давайте это все и запишем. Сначала мы конструируем пустой объект result, а потом можем поменять его поля re и im, но можем сразу написать правильный конструктор, то есть сложить между собой действительные части у двух наших слагаемых, и в качестве второго параметра конструктора сложить мнимые части у себя и у второго слагаемого. И теперь вернуть этот самый результат. Все это можно было записать в одну строку, просто return, «результат». Ну, теперь давайте попробуем напечатать сумму двух комплексных чисел и посмотреть, что у нас получится. Вот смотрите, у нас первое число было 1, 0, а второе 2, 3. Вывелось 3, 3, то есть все сложилось, и все работает. Теперь мы можем пользоваться операцией «плюс» для сложения комплексных чисел. А что будет, если мы попытаемся сложить комплексное число, например, с целым? Здесь у нас оно не сработало, то есть что оно сказало? Что объект типа int пришел, и у него нет поля re, и im у него, кстати, тоже нет, просто до него не дошло. То есть автоматически у вас ваше целое число не превратится в комплексное. Если вы хотите складывать с целым числом, то, например, вы должны вызывать конструктор от этого целого числа, и его действительная часть попадет в поле re, а im будет равно нулю, и вы получите то, что нужно. Вот так писать уже можно, и все будет работать. Теперь давайте посмотрим, как делается умножение. mul, multiplication, точно так же два параметра, само наше число, сам объект, и второй объект того же типа. Умножение делается несколько более хитро. Давайте попытаемся сейчас воспроизвести то, что у нас было записано на доске. Итак, давайте будем делать это по шагам, чтобы не запутаться, конечно, лучше с бумажкой все это делать для надежности. Но ничего страшного, справимся. Итак, действительная часть «себя» умножается на действительную часть второго множителя. При этом если мы перемножим между собой мнимые части, у нас что произойдет? У нас наше i возведется в квадрат, и мы получим ту самую −1. Таким образом, этот минус выносится сюда, я сразу пишу вместо плюса, вместо того чтобы писать умножение на −1, и перемножу мнимые части. Кажется, так оно должно быть. Что же произойдет с нашей мнимой частью? Для этого нужно действительную часть первого числа умножить на мнимую часть второго. И обратный процесс: действительную часть второго умножить на мнимую часть первого. Вроде бы похоже. Вот эти re и im, которые мы создали внутри метода, это локальные переменные, то есть они не являются полями какого-то объекта, ни self и ни other, это просто локальные переменные внутри метода. Для чего они нам нужны? Чтобы сконструировать объект. Комплексное число, новое опять же, в результате умножения должно создаваться новое комплексное число с этими параметрами. Давайте в этой функции введем такой способ возврата, то есть не будем конструировать объект, а потом делать ему имя какое-то и возвращать, а сразу вернем сконструированный объект. Давайте умножим b на c и попытаемся понять, правильный ли получился результат. Итак, получилось 2 + 3, это нормально, когда мы умножаем на 1. Давайте из b сделаем какое-нибудь более интересное число. −1 + 5i. Похоже ли это на правду? Здесь получается 2 − 3, вроде бы похоже. И мнимая часть тоже кажется правильной. Таким образом, мы смогли переопределить еще и операцию умножения. Даже если ваш объект имеет не математический какой-то смысл, может быть, вы хотите определить операцию умножения описания человека на описание другого человека. Это, конечно, сделать можно, но любая замена стандартной операции должна иметь однозначный, понятный для человека смысл. Скорее всего, никто из разработчиков, которые будут пользоваться вашим кодом, не поймут, что вы имели в виду, когда умножали человека на человека. Поэтому злоупотреблять заменой стандартных методов не стоит. Но у нас объект математический, для него все эти операции определены, и поэтому мы еще немножко поиграем с заменами стандартных методов, чтобы на этом примере научиться разным полезным вещам. [МУЗЫКА] [МУЗЫКА]