Могу добавить, что при реализации __add__ лучше обращаться к своему классу через self.__class__, а не напрямую через название Clock. Потому что названия классов имеют свойство меняться, и ничего внутри класса ломаться при этом не должно. class Clock(): def __init__(self, seconds) -> None: self.__seconds = seconds def __add__(self, other): return self.__class__(self.__seconds + other) a = Clock(100) b = a + 100 print(b.__dict__)
@@minecraftlp6306 В твоем варианте меняется сам объект. Тогда в примере выше (b = a + 100) получится, что в результате такого "сложения" изменится сам объект "a" и еще оба идентификатора (и "a" и "b") будут ссылаться на один и тот же объект
Думаю, так будет уже лучше, я вынес в отдельную функцию основу кода и в методах уже вызывал ее и писал знаки def met(self, other): if not isinstance(other, (int, Clock)): raise ArithmeticError("Допускаешь ошибку") sc = other if isinstance(other, Clock): sc = other.seconds return sc def __sub__(self, other): sc = self.met(other) return Clock(self.seconds - sc) def __mul__(self, other): sc = self.met(other) return Clock(self.seconds * sc) def __floordiv__(self, other): sc = self.met(other) return Clock(self.seconds // sc)
При нахождении переменной h в функции get_time нет необходимости искать остаток от деления на 24. Ведь ты сам в инициализаторе сделал так, чтобы секунды не превышали число 86400. А исходя из этого, мы имеем то, что количество секунд в переводе на часы никогда не превысят отметки "24 часа".
Я единственный кто пытается решить задачу с помощью вспомогательного метода класса ? На сколько я понял и ознакомился с темой Дескрипторы созданы не для этих целей , но их кончено же можно и так прописать . На самом то деле они для больше подходят для управления доступом к атрибутам, а не для выполнения арифметических операций. Вот мое решение , сразу скажу не без посторонней помощи , я не гейний ))) сам голову долго ломал как лучше class Clock: __DAY = 86400 # Количество секунд в сутках def __init__(self, seconds: int): if not isinstance(seconds, int): raise TypeError('секунды должны быть целым числом') self.seconds = seconds % self.__DAY def get_time(self): s = self.seconds % 60 m = (self.seconds // 60) % 60 h = (self.seconds // 3600) % 24 return f'{self.__convert(h)}:{self.__convert(m)}:{self.__convert(s)}' @staticmethod def __convert(x): return str(x).rjust(2, '0') def _operate_time(self, other, operator): if not isinstance(other, int): raise TypeError('Операция допустима только с целыми числами') new_seconds = operator(self.seconds, other) % self.__DAY return Clock(new_seconds) def __add__(self, other): return self._operate_time(other, lambda x, y: x + y) def __sub__(self, other): return self._operate_time(other, lambda x, y: x - y) def __mul__(self, other): return self._operate_time(other, lambda x, y: x * y) def __truediv__(self, other): if other == 0: raise ZeroDivisionError("Нельзя делить на ноль") return self._operate_time(other, lambda x, y: int(x / y)) def __floordiv__(self, other): if other == 0: raise ZeroDivisionError("Нельзя делить на ноль") return self._operate_time(other, lambda x, y: x // y) def __mod__(self, other): if other == 0: raise ZeroDivisionError("Нельзя брать остаток от деления на ноль") return self._operate_time(other, lambda x, y: x % y) def __radd__(self, other): return self.__add__(other) def __rsub__(self, other): if not isinstance(other, int): raise TypeError('Можно отнимать только от целого числа') return Clock((other - self.seconds) % self.__DAY) def __rmul__(self, other): return self.__mul__(other)
Вопрос: что будет если сложить два класса Clock(c1+c2) что вызовется(оба метода определены): с1.__add__(c2) или c2.__rand__(c1). В вашем примере разницы нет, но на практике это могут быть 2 разных класса и от порядка зависит то, какой класс возвращать.
поправьте меня, если я не до конца правильно понимаю. Мне кажется что 5:08 минуте метод __get_formatted правильнее сделать статическим методом а не методом класса потому что он используется в методе экземпляра класса (в объекте). Понятно что будет работать и так и так, но все же.... ?
Ебать, хорош, мужик. Также подумал. Хорошо что не один такой. Вот так прописал: @staticmethod def ver_sec(x): if not isinstance(x, int): raise TypeError('need int')
Для класса Point из предыдущих уроков: class Points: # создание класса def __init__(self, x,y,z): # инициализация self.coords = [x,y,z] def __str__(self): # применяется, если объект класса делают строкой return (f'Координаты точки: '+ f'X = {self.coords[0]} '+ f'Y = {self.coords[1]} '+ f'Z = {self.coords[2]}')
def _operate(first,other,action): if isinstance(other, (int, float)): # проверяет тип второго аргумента return Points((eval(f'{first.coords[0]} {action} {other}')), (eval(f'{first.coords[1]} {action} {other}')), (eval(f'{first.coords[2]} {action} {other}'))) elif type(other) == Points: # позволит прибавлять к объекту другой объект класса return Points((eval(f'{first.coords[0]} {action} {other.coords[0]}')), (eval(f'{first.coords[1]} {action} {other.coords[1]}')), (eval(f'{first.coords[2]} {action} {other.coords[2]}'))) else: raise TypeError('второй аргумент должен быть числом или объектом класса') def __add__(self, other): # вызывается, если к объекту класса применить сложение return self._operate(other, '+') def __sub__(self, other): # вызывается, если к объекту класса применить вычитание return self._operate(other, '-') def __mul__(self, other): # вызывается, если к объекту класса применить умножение return self._operate(other, '*') def __truediv__(self, other): # вызывается, если к объекту класса применить деление return self._operate(other, '/') point1 = Points(16,42,12) point2 = Points(1,1,1) point3 = Points(3,3,3) point4 = point1 * point3 - point2 print(point4) ----------------вывод---------------- Координаты точки: X = 47 Y = 125 Z = 35
Вопрос, почему в методе __add__ мы в место того что бы создавать новый обьект класса Clock не можем просто увеличить поле seconds на передаваемое в параметрах число? ведь в таком случае программа будет работать гораздо быстрее, т.к. на создание нового обьекта класса уходит гораздо больше (по компьютерным меркам) времени?
Насколько я понимаю, именно потому, что мы не всегда хотим при сложении менять исходный экземпляр класса. Например: c2 = c1 + 100 тут нам не нужно, чтобы менялся c1.seconds. А нужно чтобы функция возвращала новый экземпляр и присваивала его переменной c2
Вроде бы принято при неподходящем типе операндов в арифметических методах не вызывать исключение, а возвращать константу NotImplemented. Это позволяет попытаться обратиться к соответствующему методу второго операнда прежде чем выкидывать ошибку.
Здравствуйте. Я понимаю, что в методе __add__ новый экземпляр создаётся для того, чтобы можно было присвоить сложению другую переменную. Но, если мы выполним c1 = c1 + int, и, допустим, у c1 были бы другие локальные свойства, они ведь пропадут. Как можно было это исправить? Как-то проверить совпадает ли имя присваемой переменной и имя переменной с которй складываем и затем передать ей словарь __dict__ с локальными атрибутами?
Можно тупой вопрос)) Вы говорили, что лучше использовать ссылку на класс, когда его где-то используешь в коде, чтобы не положить программу при замене названия. Можно ли тут использовать cls вместо Clock ?
Сергей, а почему при использовании .rjust(2, 0) вы говорите, что нолик добавляется справа? Ведь он добавляется слева от цифр. Можно конечно предположить, что цифра смотрит на нас "лицом" и то, что мы воспринимаем как слева от неё, это на самом деле справа, но в таком случае при объяснении __add__(self, other) вы объясняете, что other это то, что стоит права от c1
@@selfedu_rus но ведь и метод почему-то начинается на r, а тот, что добавляет символы справа на l, какая-то путаница. А ещё подскажите, планируете ли вы делать на stepik курсы по джанго, базам данных и всему тому, что необходимо для бэкенда? Ролики на эти темы у вас на канале есть, но без практических задач сухая теория совсем не усваивается
@@selfedu_rus надеюсь, что решение будет найдено. Низкий вам поклон и благодарность за то, что вы для всех нас делаете, учиться у вас - одно удовольствие!
А зачем нужна __get_formated() функция? Проще же так: return f'{h:02}:{m:02}:{s:02}'. Ладно, буду считать, что это тренировка использования декоратора @classmethod
А не проще вместо __get_formatted сделать так: def get_time(self): """Получить текущее время.""" s = self.seconds % 60 m = self.seconds % 3600 // 60 h = self.seconds // 3600 return f'{h:02d}:{m:02d}:{s:02d}' И "h" зачем "% 24", когда это уже в __init__ сделали?
Да, и я попровал в методе __add__ не создавать новый обьект класса, а изменять поле self.seconds и возвращать ссылку на текущий обьект класса. множественное сложение, и все другие виды сложений так же прекрасно работают. И программа соответственно должна работать быстрее, получается одни плюсы
@@СарматПересветов вы в add делаете так: return self.seconds + other тогда да, в самом объекте ничего меняться не будет. Это мой ляп. Но объект все же нужен (в общем случае), т.к. хорошо, что здесь числа складываются, а если бы мы комплексные числа складывали? Тогда без объекта не работало бы.
Добрый день. Сориентируйте, пожалуйста, почему мы присваиваем функции get_formatted уровень класса @classmethod? В нем же не используются атрибуты класса, тот же __DAY. Заранее благодарна!
Благодарю за столь оперативный ответ! У меня появился ещё вопрос, если позволите. В коде мы в явном виде прописываем класс Clock, когда нам надо создавать экземпляры объектов для сложения в ф-ии __add__ и когда делаем проверку на принадлежность other к классу. Правильно ли я понимаю, что в данном случае мы никоим образом не можем уйти от прямого обращения к классу, заменив на cls или self, как делали в других уроках? Ведь если наименование класса Clock изменится, то потребуется менять наименование во всех местах, где было прямое обращение. Надеюсь, мой вопрос понятен :)
После переопределения метода __iadd__ решил убедиться, что новый экземпляр с1 не создается. Для этого выполнил печать следующим образом: c1 = Clock(1000) print("c1 =", c1, "=", c1.get_time()) c1 += 100 print("c1 =", c1, "=", c1.get_time()) в результате получил: c1 = = 00:16:40 __iadd__ c1 = = 00:20:00 т.е., адрес изменился, значит, новый экземпляр таки был создан?
Комментарий неправильный, я ошибся при копировании реализации метода. после исправления ошибки получил: c1 = = 00:21:40 __iadd__ c1 = = 00:23:20 Новый экземпляр не создается
А зачем часы ещё делить по остатку на 24, если в принципе их не может быть больше 24, благодаря делению секунд на __DAY, или я чего-то не понимаю? И почему мы используем метод класса вместо статик метода, если мы не работаем с атрибутами класса?
А можно подсказку по том, как без дублирования кода засунуть все арифметические операции в класс, ведь по сути код для маг метода __add__ отличается от __sub__, __mul__, __truediv__ только названием метода и конкретной операцией. Через каррирование загнав все маг методы в словарь с соответствующим оператором, или видео урок про дескрипторы как раз здесь и можно применить, или я вовсе перемудрил и решение куда проще? Проверки на int, Clock тоже можно было засунуть в отдельный @classmetod для соблюдения "dry" это бы упростило читабельность и громоздкость кода? Заранее спасибо..
class Desc: def __init__(self): self.opdct={'add':'+','sub':'-','mul':'*','truediv':'/'} def __set_name__(self,owner,name): name=name.strip('_') self.prefix=name[0] if name.startswith(('i','r')) else None self.name=name[1:] if self.prefix else name def __get__(self,obj,owner): def wrapp(other): if type(other) in (int,float): n1,n2=obj.num,other else: n1,n2=obj.num,other.num if not self.prefix: return owner(self.op(self.opdct[self.name],n1,n2)) if self.prefix=='r': return owner(self.op(self.opdct[self.name],n2,n1)) if self.prefix=='i': setattr(obj,'num',self.op(self.opdct[self.name],n1,n2)) return obj return wrapp
@staticmethod def op(opsign,n1,n2): return eval(f'{n1}{opsign}{n2}') class A: __add__=Desc() __sub__=Desc() __mul__=Desc() __truediv__=Desc() __radd__=Desc() __rsub__=Desc() __rmul__=Desc() __rtruediv__=Desc() __iadd__=Desc() __isub__=Desc() __imul__=Desc() __itruediv__=Desc() def __init__(self,num): но это все самая первая проба....развитие и улучшение в моих решениях на степике суть метода использование non-data дескрипторов и замыканий в _get_ типовая конструкция такая def __get__(self,object,owner): def inner(other): return ... return inner а для работы с методами такая еще конструкция return owner(getattr(object.param,self.name)(other.param))-если нам например нужно получить результат арифметической операции над объектом как новый объект с параметром -результатом операции над 2 объектами КОРОЧЕ ПАСАНЫ ,,,,Я ЭТИ ВАШИ МАГМЕТОДЫ НА ДЕСКРИПТОРЕ ВЕРТЕЛ КАК ХОТЕЛ))))))
А если имя класса Clock изменится, как тогда поведет себя строчка return Clock(self.seconds + other). Может бы правильнее return sels.__class__(self.seconds + other)?
Почему вначале при сложении мы создаём новый Экземпляр Класса, а потом (+=) мы просто меняем значение Атрибута Экземпляра Класса. Почему в первом случае мы не можем поменять? Зачем нам создавать новый Экземпляр Класса.
🤣все с точностью донаоборот....не прописывать каждую функцию и декорировать ее,а создать дескриптор который будет описывать всю шоблу этих функций)))))....просто поэкспериментируй с конструкцией __get__(): def inner(other): return ..... return inner
А можно хоть одну прогу написать, чтобы без raise? Чтобы заставить пользователя ввести именно то, что необходимо ввести и не прерывать, как сумасшедший выполнение программы (особенно в примере использования объекта property)? Кому такая программа будет нужна, если на каждый неправильный шаг юзверя она будет вываливаться? Так себе подход, короче.
Насчёт домашки, можно либо сделать класс метод который считывает other и его уже превращает в цифарки и в ариф. методах делать self.seconds + func(other), либо сделать декоратор, и обернуть ариф. методы Собственно вот мой второй путь from functools import wraps class Clock: __DAY = 86400 def __init__(self, seconds: int): if not isinstance(seconds, int): raise TypeError("На*** иди") self.seconds = seconds % self.__DAY def get_time(self): s = self.seconds % 60 m = self.seconds // 60 % 60 h = self.seconds // 3600 return f"{h:02}:{m:02}:{s:02}" def __calculate(func): @wraps(func) def wrapper(self, other): if not isinstance(other, (int, Clock)): raise TypeError("Одумайся грешник") if isinstance(other, Clock): other = other.seconds return Clock(func(self.seconds, other)) return wrapper
@__calculate def __add__(self, other): return self + other def __radd__(self, other): return self + other @__calculate def __sub__(self, other): return self - other @__calculate def __mul__(self, other): return self * other @__calculate def __floordiv__(self, other): return self // other
Кажется, что было бы правильнее в методе __add__ поступить также как и с __iadd__. Так, в методе __add__ нам не придется создавать новый экземпляр (мы можем просто увеличить атрибут seconds, а затем вернуть self). Или мне не правильно кажется?) def __add__(self, other): if not isinstance(other, (int, Clock)): raise ArithmeticError("Adding number must be int") sc = other if isinstance(other, Clock): sc = other.seconds self.seconds += sc return self # return Clock(self.seconds + sc)
А Вы прогоните свой вариант на объектах класса например p1 = Class(100), p2 = Class(200) и p3 = p1 + p2, а потом выведите что находится в p1, p2, p3 и поймете почему именно у __iadd__ другая реализация
Вроде неплохо получилось def universal_operation(self, other, operation: str, for_myself: bool): if not isinstance(other, (int, Clock)): raise ArithmeticError(f'unsupported operand type(s) for +: {type(self)} and {type(other)}') sc = other if isinstance(other, Clock): sc = other.seconds
а зачем мы в качестве возвращаемого значения метода __add__ устанавливаем НОВЫЙ объект класса Clock с увеличенным количеством секунд: return Clock(self.seconds + other) вместо того, чтобы просто увеличить значение для текущего объекта: return self.seconds + other ?
или это просто демонстрация того, как можно манипулировать данными, а нам на практике уже нужно будет действовать на своё усмотрение в зависимости от ситуации?
В данной реализации вернется количество секунд, а не экземпляр класса Clock. Если требуется изменить атрибут конкретного класса и вернуть экземпляр этого же класса (без создания нового), предлагаю примерно такой вариант: def __add__(self, other): if not isinstance(other, (int, Clock)): raise ArithmeticError("Атрибут должен быть целым числом (int) и объектом класса Clock") if isinstance(other, int): self._seconds += other if isinstance(other, Clock): self._seconds += other._seconds return self
Не понятно как происходит вызов метода radd. Если я выполняю с1 + 100, происходит вызов с1.__add__(100). Вызывая 100 + с1, я ожидаю вызов 100.__add__(c1), т. Е метод класса int. Как интерпретатор понимает, что нужно вызвать именно radd нашего класса?
Не понятно, если эти методы из коробки идут, то зачем в них еще столько кода писать, почему они сразу в две или одну строчку не идут? или я не про то? )
не про то, можно их вообще не переопределять, если не нужно, в противном случае прописываем свой алгоритм, а сколько строчек кода - это зависит от задачи
попытался превратить магию методов в вуду дескрипторов и замыканий class Desc: def __init__(self): self.opdct={'add':'+','sub':'-','mul':'*','truediv':'/'} def __set_name__(self,owner,name): name=name.strip('_') self.prefix=name[0] if name.startswith(('i','r')) else None self.name=name[1:] if self.prefix else name def __get__(self,obj,owner): def wrapp(other): if type(other) in (int,float): n1,n2=obj.num,other else: n1,n2=obj.num,other.num if not self.prefix: return owner(self.op(self.opdct[self.name],n1,n2)) if self.prefix=='r': return owner(self.op(self.opdct[self.name],n2,n1)) if self.prefix=='i': setattr(obj,'num',self.op(self.opdct[self.name],n1,n2)) return obj return wrapp
@staticmethod def op(opsign,n1,n2): return eval(f'{n1}{opsign}{n2}') class A: __add__=Desc() __sub__=Desc() __mul__=Desc() __truediv__=Desc() __radd__=Desc() __rsub__=Desc() __rmul__=Desc() __rtruediv__=Desc() __iadd__=Desc() __isub__=Desc() __imul__=Desc() __itruediv__=Desc() def __init__(self,num): но это все самая первая проба....развитие и улучшение в моих решениях на степике суть метода использование non-data дескрипторов и замыканий в __get__ типовая конструкция такая def __get__(self,object,owner): def inner(other): return ... return inner а для работы с методами такая еще конструкция return owner(getattr(object.param,self.name)(other.param))-если нам например нужно получить результат арифметической операции над объектом как новый объект с параметром -результатом операции над 2 объектами КОРОЧЕ ПАСАНЫ ,,,,Я ЭТИ ВАШИ МАГМЕТОДЫ НА ДЕСКРИПТОРЕ ВЕРТЕЛ КАК ХОТЕЛ))))))
почему на 23 строчке нельзя было написать if isinstance(other, Clock): other = other.seconds return Clock(self.seconds + other) без введения доп переменной
Очень советую избегать этих магических методов (именно этих 4-х). Лучше сделайте отдельные обычные методы add(), sub() или назовите какими-нибудь increase(), decrease(). И в методах просто соответственно увеличивайте или уменьшайте свойство seconds. Если же вы все-таки решили добавить логику сложения объектов с этими маг-методами, то обязательно протестируйте результат на всех возможных вариантах: объект справа от плюса, слева от плюса, складываются два объекта и результат присваивается третьему. И обязательно каждый раз проверяйте все свойства (в примере из видео это одно свойство seconds) всех объектов. А вот если вы создадите дочерний класс от этого, гемор вам обеспечен 😁
Меня месяц тревожит вопрос, проиллюстрирую на примере из видео: c1 = Clock(1000) c1 = c1 + 100 Как сделать, чтобы работало и c1 = 100 + c1 ? Заранее спасибо)
У меня освободилось кучу времени, и прохожу курс в степик. (Но я очень далеко... На 3.7... ((() Этот курс будет там еще долго? Уважаю ваш труд. Вы гений.
@@selfedu_rus вряд-ли увижу ответ, но попытаюсь) В __radd__ мы получаем self и изменяя экземпляр через него, по сути методом и запишем значение, или я не прав? self ведь получается берет как раз что нам и нужно...
Как понять незначищий ноль справа , а потом говорите если 1 2 или то будет 0 1 0 2 и тд., где логика? Там помойму число больше нуля вправа едет а нули влево
Материал и подача хорошие, но английский плох. Рекомендую подучить произношение слов, которые используются в программировании. Неправильное произношение вызывает недоумение у незнающих людей. Ещё было бы полезно объяснять полное название методов, например mul - multiply(умножать). В данном видео метод __add__ читается как "эд" (добавить)
опять бессмысленное усложнение объяснения на ровном месте, с этими секундами, сначала надо понять суть на примитиве, потом рассчитывать космический корабль, часть ресурсов мозга уходит что бы вникнуть ещё и в пример ...
Просто кладезь знаний 👍
Следующая ступенька, после ООП, паттерны 🙏
Идеальный урок.. Моё искреннее почтение Вашему труду... Передача Ваших знаний в массы бесценно...
Могу добавить, что при реализации __add__ лучше обращаться к своему классу через self.__class__, а не напрямую через название Clock. Потому что названия классов имеют свойство меняться, и ничего внутри класса ломаться при этом не должно.
class Clock():
def __init__(self, seconds) -> None:
self.__seconds = seconds
def __add__(self, other):
return self.__class__(self.__seconds + other)
a = Clock(100)
b = a + 100
print(b.__dict__)
я бы сделал удобочитаемей:
def __add__(self, sec):
self.__seconds += sec
return self
@@minecraftlp6306 В твоем варианте меняется сам объект. Тогда в примере выше (b = a + 100) получится, что в результате такого "сложения" изменится сам объект "a" и еще оба идентификатора (и "a" и "b") будут ссылаться на один и тот же объект
Очень хорошо объяснили, а то я не мог понять как это работает, после вашего объяснения все стало на свои места, спасибо за ваш труд
Спасибо. Как всегда все доходчиво и понятно!
Вторая неделя изучения ооп!!!!1!1!?1!1?2++1(#-1)2
Если говорить короче,то иду хорошо, спасибо огромное автору за отличное объяснение материала❤
Думаю, так будет уже лучше, я вынес в отдельную функцию основу кода и в методах уже вызывал ее и писал знаки
def met(self, other):
if not isinstance(other, (int, Clock)):
raise ArithmeticError("Допускаешь ошибку")
sc = other
if isinstance(other, Clock):
sc = other.seconds
return sc
def __sub__(self, other):
sc = self.met(other)
return Clock(self.seconds - sc)
def __mul__(self, other):
sc = self.met(other)
return Clock(self.seconds * sc)
def __floordiv__(self, other):
sc = self.met(other)
return Clock(self.seconds // sc)
Сергей, спасибо за уроки!!!
Снимаю шляпю и самый низкий поклон до колени !!!
Спасибо. Очень интересно.
класс! Серёга лучший!
Очень доступно, спасибо!
Спасибо. Ничего себе, чего можно наворотить.
При нахождении переменной h в функции get_time нет необходимости искать остаток от деления на 24. Ведь ты сам в инициализаторе сделал так, чтобы секунды не превышали число 86400. А исходя из этого, мы имеем то, что количество секунд в переводе на часы никогда не превысят отметки "24 часа".
Привычка все проверять на всех уровнях.
спасибо за урок!
Это гениально
В промежутке 15:00-15:10 сказано что временная переменная t2 ведет на новый клас, но t2 = Clock(3000 + 3000) это уже екземпляр класа
оговорка, экземпляр класса
Ураа, домашка)))
Я единственный кто пытается решить задачу с помощью вспомогательного метода класса ? На сколько я понял и ознакомился с темой Дескрипторы созданы не для этих целей , но их кончено же можно и так прописать . На самом то деле они для больше подходят для управления доступом к атрибутам, а не для выполнения арифметических операций. Вот мое решение , сразу скажу не без посторонней помощи , я не гейний ))) сам голову долго ломал как лучше
class Clock:
__DAY = 86400 # Количество секунд в сутках
def __init__(self, seconds: int):
if not isinstance(seconds, int):
raise TypeError('секунды должны быть целым числом')
self.seconds = seconds % self.__DAY
def get_time(self):
s = self.seconds % 60
m = (self.seconds // 60) % 60
h = (self.seconds // 3600) % 24
return f'{self.__convert(h)}:{self.__convert(m)}:{self.__convert(s)}'
@staticmethod
def __convert(x):
return str(x).rjust(2, '0')
def _operate_time(self, other, operator):
if not isinstance(other, int):
raise TypeError('Операция допустима только с целыми числами')
new_seconds = operator(self.seconds, other) % self.__DAY
return Clock(new_seconds)
def __add__(self, other):
return self._operate_time(other, lambda x, y: x + y)
def __sub__(self, other):
return self._operate_time(other, lambda x, y: x - y)
def __mul__(self, other):
return self._operate_time(other, lambda x, y: x * y)
def __truediv__(self, other):
if other == 0:
raise ZeroDivisionError("Нельзя делить на ноль")
return self._operate_time(other, lambda x, y: int(x / y))
def __floordiv__(self, other):
if other == 0:
raise ZeroDivisionError("Нельзя делить на ноль")
return self._operate_time(other, lambda x, y: x // y)
def __mod__(self, other):
if other == 0:
raise ZeroDivisionError("Нельзя брать остаток от деления на ноль")
return self._operate_time(other, lambda x, y: x % y)
def __radd__(self, other):
return self.__add__(other)
def __rsub__(self, other):
if not isinstance(other, int):
raise TypeError('Можно отнимать только от целого числа')
return Clock((other - self.seconds) % self.__DAY)
def __rmul__(self, other):
return self.__mul__(other)
Вопрос: что будет если сложить два класса Clock(c1+c2) что вызовется(оба метода определены): с1.__add__(c2) или c2.__rand__(c1). В вашем примере разницы нет, но на практике это могут быть 2 разных класса и от порядка зависит то, какой класс возвращать.
спасибо!👏👍
поправьте меня, если я не до конца правильно понимаю. Мне кажется что 5:08 минуте метод __get_formatted правильнее сделать статическим методом а не методом класса потому что он используется в методе экземпляра класса (в объекте). Понятно что будет работать и так и так, но все же.... ?
Ебать, хорош, мужик. Также подумал. Хорошо что не один такой. Вот так прописал:
@staticmethod
def ver_sec(x):
if not isinstance(x, int):
raise TypeError('need int')
Для класса Point из предыдущих уроков:
class Points: # создание класса
def __init__(self, x,y,z): # инициализация
self.coords = [x,y,z]
def __str__(self): # применяется, если объект класса делают строкой
return (f'Координаты точки:
'+
f'X = {self.coords[0]}
'+
f'Y = {self.coords[1]}
'+
f'Z = {self.coords[2]}')
def _operate(first,other,action):
if isinstance(other, (int, float)): # проверяет тип второго аргумента
return Points((eval(f'{first.coords[0]} {action} {other}')),
(eval(f'{first.coords[1]} {action} {other}')),
(eval(f'{first.coords[2]} {action} {other}')))
elif type(other) == Points: # позволит прибавлять к объекту другой объект класса
return Points((eval(f'{first.coords[0]} {action} {other.coords[0]}')),
(eval(f'{first.coords[1]} {action} {other.coords[1]}')),
(eval(f'{first.coords[2]} {action} {other.coords[2]}')))
else:
raise TypeError('второй аргумент должен быть числом или объектом класса')
def __add__(self, other): # вызывается, если к объекту класса применить сложение
return self._operate(other, '+')
def __sub__(self, other): # вызывается, если к объекту класса применить вычитание
return self._operate(other, '-')
def __mul__(self, other): # вызывается, если к объекту класса применить умножение
return self._operate(other, '*')
def __truediv__(self, other): # вызывается, если к объекту класса применить деление
return self._operate(other, '/')
point1 = Points(16,42,12)
point2 = Points(1,1,1)
point3 = Points(3,3,3)
point4 = point1 * point3 - point2
print(point4)
----------------вывод----------------
Координаты точки:
X = 47
Y = 125
Z = 35
Вопрос, почему в методе __add__ мы в место того что бы создавать новый обьект класса Clock не можем просто увеличить поле seconds на передаваемое в параметрах число? ведь в таком случае программа будет работать гораздо быстрее, т.к. на создание нового обьекта класса уходит гораздо больше (по компьютерным меркам) времени?
Насколько я понимаю, именно потому, что мы не всегда хотим при сложении менять исходный экземпляр класса. Например:
c2 = c1 + 100
тут нам не нужно, чтобы менялся c1.seconds. А нужно чтобы функция возвращала новый экземпляр и присваивала его переменной c2
Вроде бы принято при неподходящем типе операндов в арифметических методах не вызывать исключение, а возвращать константу NotImplemented. Это позволяет попытаться обратиться к соответствующему методу второго операнда прежде чем выкидывать ошибку.
Перегрузки операторов в с++ и подобных языках аналогично работают
Здравствуйте. Я понимаю, что в методе __add__ новый экземпляр создаётся для того, чтобы можно было присвоить сложению другую переменную. Но, если мы выполним c1 = c1 + int, и, допустим, у c1 были бы другие локальные свойства, они ведь пропадут. Как можно было это исправить? Как-то проверить совпадает ли имя присваемой переменной и имя переменной с которй складываем и затем передать ей словарь __dict__ с локальными атрибутами?
вроде и понял, но повторить или добавить свое точно не получится, тем кто понимает и применяет такое сразу, завидую)
Можно тупой вопрос)) Вы говорили, что лучше использовать ссылку на класс, когда его где-то используешь в коде, чтобы не положить программу при замене названия.
Можно ли тут использовать cls вместо Clock ?
Вместо Clock используй self.__class__, перед и после class двойное нижнее подчеркивание
Сергей, а почему при использовании .rjust(2, 0) вы говорите, что нолик добавляется справа? Ведь он добавляется слева от цифр.
Можно конечно предположить, что цифра смотрит на нас "лицом" и то, что мы воспринимаем как слева от неё, это на самом деле справа, но в таком случае при объяснении __add__(self, other) вы объясняете, что other это то, что стоит права от c1
да я просто оговорился! Конечно, слева! ))
@@selfedu_rus но ведь и метод почему-то начинается на r, а тот, что добавляет символы справа на l, какая-то путаница.
А ещё подскажите, планируете ли вы делать на stepik курсы по джанго, базам данных и всему тому, что необходимо для бэкенда? Ролики на эти темы у вас на канале есть, но без практических задач сухая теория совсем не усваивается
думал про Django, но на Stepik сложно сделать хороший курс по нему, т.к. это ж фреймворк. Это останавливает.
@@selfedu_rus надеюсь, что решение будет найдено.
Низкий вам поклон и благодарность за то, что вы для всех нас делаете, учиться у вас - одно удовольствие!
@@selfedu_rus Я думаю если найдете решение и сделайте курс платным будет пользоваться спросом так как нормального курса по джанго нет сейчас
А зачем мы метод "get_formated" сделали методом класса? Статическим не правильнее?
А зачем нужна __get_formated() функция? Проще же так: return f'{h:02}:{m:02}:{s:02}'. Ладно, буду считать, что это тренировка использования декоратора @classmethod
А не проще вместо __get_formatted сделать так:
def get_time(self):
"""Получить текущее время."""
s = self.seconds % 60
m = self.seconds % 3600 // 60
h = self.seconds // 3600
return f'{h:02d}:{m:02d}:{s:02d}'
И "h" зачем "% 24", когда это уже в __init__ сделали?
Да, и я попровал в методе __add__ не создавать новый обьект класса, а изменять поле self.seconds и возвращать ссылку на текущий обьект класса. множественное сложение, и все другие виды сложений так же прекрасно работают. И программа соответственно должна работать быстрее, получается одни плюсы
так у вас и значения в объектах, участвующих в сложениях будут тогда меняться
@@selfedu_rus ну, вот сейчас посмотрел, не изменились значения в обьектах участвующих в сложении, все работает так как и должно
@@СарматПересветов вы в add делаете так:
return self.seconds + other
тогда да, в самом объекте ничего меняться не будет. Это мой ляп. Но объект все же нужен (в общем случае), т.к. хорошо, что здесь числа складываются, а если бы мы комплексные числа складывали? Тогда без объекта не работало бы.
@@selfedu_rus Понял, спасибо)
Чтобы не было дублирования кода - оставшиеся операторы сделать через дескрипторы?
Добрый день. Сориентируйте, пожалуйста, почему мы присваиваем функции get_formatted уровень класса @classmethod? В нем же не используются атрибуты класса, тот же __DAY. Заранее благодарна!
да, здесь вполне можно прописать статик, все верно!
Благодарю за столь оперативный ответ!
У меня появился ещё вопрос, если позволите. В коде мы в явном виде прописываем класс Clock, когда нам надо создавать экземпляры объектов для сложения в ф-ии __add__ и когда делаем проверку на принадлежность other к классу. Правильно ли я понимаю, что в данном случае мы никоим образом не можем уйти от прямого обращения к классу, заменив на cls или self, как делали в других уроках? Ведь если наименование класса Clock изменится, то потребуется менять наименование во всех местах, где было прямое обращение. Надеюсь, мой вопрос понятен :)
Нашла ответ на свой вопрос у Евгения ниже)
Ещё раз спасибо за Вашу подачу материала!
Было бы элегантней написать sc = other.seconds if isinstance(other, Click) else other)
После переопределения метода __iadd__ решил убедиться, что новый экземпляр с1 не создается. Для этого выполнил печать следующим образом:
c1 = Clock(1000)
print("c1 =", c1, "=", c1.get_time())
c1 += 100
print("c1 =", c1, "=", c1.get_time())
в результате получил:
c1 = = 00:16:40
__iadd__
c1 = = 00:20:00
т.е., адрес изменился, значит, новый экземпляр таки был создан?
Комментарий неправильный, я ошибся при копировании реализации метода. после исправления ошибки получил:
c1 = = 00:21:40
__iadd__
c1 = = 00:23:20
Новый экземпляр не создается
А зачем часы ещё делить по остатку на 24, если в принципе их не может быть больше 24, благодаря делению секунд на __DAY, или я чего-то не понимаю? И почему мы используем метод класса вместо статик метода, если мы не работаем с атрибутами класса?
на 24 делю для надежности, чтобы точно не было больше (привычка везде делать "защиту от дурака"), а метод да, здесь вполне и статик подойдет.
А можно подсказку по том, как без дублирования кода засунуть все арифметические операции в класс, ведь по сути код для маг метода __add__ отличается от __sub__, __mul__, __truediv__ только названием метода и конкретной операцией. Через каррирование загнав все маг методы в словарь с соответствующим оператором, или видео урок про дескрипторы как раз здесь и можно применить, или я вовсе перемудрил и решение куда проще?
Проверки на int, Clock тоже можно было засунуть в отдельный @classmetod для соблюдения "dry" это бы упростило читабельность и громоздкость кода?
Заранее спасибо..
Я думаю словарь нужно составить 'операция':'знак операции' и использовать eval()
class Desc:
def __init__(self):
self.opdct={'add':'+','sub':'-','mul':'*','truediv':'/'}
def __set_name__(self,owner,name):
name=name.strip('_')
self.prefix=name[0] if name.startswith(('i','r')) else None
self.name=name[1:] if self.prefix else name
def __get__(self,obj,owner):
def wrapp(other):
if type(other) in (int,float):
n1,n2=obj.num,other
else:
n1,n2=obj.num,other.num
if not self.prefix:
return owner(self.op(self.opdct[self.name],n1,n2))
if self.prefix=='r':
return owner(self.op(self.opdct[self.name],n2,n1))
if self.prefix=='i':
setattr(obj,'num',self.op(self.opdct[self.name],n1,n2))
return obj
return wrapp
@staticmethod
def op(opsign,n1,n2):
return eval(f'{n1}{opsign}{n2}')
class A:
__add__=Desc()
__sub__=Desc()
__mul__=Desc()
__truediv__=Desc()
__radd__=Desc()
__rsub__=Desc()
__rmul__=Desc()
__rtruediv__=Desc()
__iadd__=Desc()
__isub__=Desc()
__imul__=Desc()
__itruediv__=Desc()
def __init__(self,num):
но это все самая первая проба....развитие и улучшение в моих решениях на степике
суть метода использование non-data дескрипторов и замыканий в _get_
типовая конструкция такая
def __get__(self,object,owner):
def inner(other):
return ...
return inner
а для работы с методами такая еще конструкция return owner(getattr(object.param,self.name)(other.param))-если нам например нужно получить результат арифметической операции над объектом как новый объект с параметром -результатом операции над 2 объектами
КОРОЧЕ ПАСАНЫ ,,,,Я ЭТИ ВАШИ МАГМЕТОДЫ НА ДЕСКРИПТОРЕ ВЕРТЕЛ КАК ХОТЕЛ))))))
А если имя класса Clock изменится, как тогда поведет себя строчка return Clock(self.seconds + other). Может бы правильнее return sels.__class__(self.seconds + other)?
да, можно так
@@selfedu_rus вопрос не в том, что так можно. Вопрос в том, что так правильнее. Снимается зависимость от имени класса
В проверке isinstance значения bool проходят как int. Возможно корректнее через type?
да, если нужно четкое сравнение на типы, то конечно, type
@selfedu Серёга молодец конечно. Но ты не сказал зачем нужны эти методы на практике.
Почему вначале при сложении мы создаём новый Экземпляр Класса, а потом (+=) мы просто меняем значение Атрибута Экземпляра Класса.
Почему в первом случае мы не можем поменять? Зачем нам создавать новый Экземпляр Класса.
Спасибо крутое ДЗ:
class Clock:
__DAY = 86400
def __init__(self,seconds:int):
if not isinstance(seconds,(int,float)):
raise TypeError("Must be int or flot")
self.seconds = seconds % self.__DAY
def get_time(self):
s = self.seconds % 60
m = (self.seconds // 60) % 60
h = (self.seconds // 3600) % 24
return f"{self.__get_formatted(h)}:{self.__get_formatted(m)}:{self.__get_formatted(s)}"
@classmethod
def __get_formatted(cls,x):
return str(x).rjust(2, "0")
def process(func):
def inner(self, other):
if not isinstance(other, (int, Clock)):
raise ArithmeticError(" Right operant must be int")
sc = other
if isinstance(other, Clock):
sc = other.seconds
return Clock(func(self.seconds, sc))
return inner
@process
def __add__(self, other):
return self + other
def __radd__(self, other):
return self + other
@process
def __sub__(self, other):
return self - other
@process
def __mul__(self, other):
return self * other
@process
def __truediv__(self, other):
return self / other
c1 = Clock(1000)
c2 = Clock(2000)
print(c1.get_time())
print(c2.get_time())
c3 = c1 + c2
print(c3.get_time())
c4 = c3 - c2
print(c4.get_time())
c5 = c2 / c1
print(c5.get_time())
🤣все с точностью донаоборот....не прописывать каждую функцию и декорировать ее,а создать дескриптор который будет описывать всю шоблу этих функций)))))....просто поэкспериментируй с конструкцией __get__():
def inner(other):
return .....
return inner
во, твой вариант мне прям понравился
Возможности f-строк в Python позволяют обойтись без метода __get_formatted() вот таким образом: {self.h:>02}
А можно хоть одну прогу написать, чтобы без raise? Чтобы заставить пользователя ввести именно то, что необходимо ввести и не прерывать, как сумасшедший выполнение программы (особенно в примере использования объекта property)? Кому такая программа будет нужна, если на каждый неправильный шаг юзверя она будет вываливаться? Так себе подход, короче.
Насчёт домашки, можно либо сделать класс метод который считывает other и его уже превращает в цифарки и в ариф. методах делать self.seconds + func(other), либо сделать декоратор, и обернуть ариф. методы
Собственно вот мой второй путь
from functools import wraps
class Clock:
__DAY = 86400
def __init__(self, seconds: int):
if not isinstance(seconds, int):
raise TypeError("На*** иди")
self.seconds = seconds % self.__DAY
def get_time(self):
s = self.seconds % 60
m = self.seconds // 60 % 60
h = self.seconds // 3600
return f"{h:02}:{m:02}:{s:02}"
def __calculate(func):
@wraps(func)
def wrapper(self, other):
if not isinstance(other, (int, Clock)):
raise TypeError("Одумайся грешник")
if isinstance(other, Clock):
other = other.seconds
return Clock(func(self.seconds, other))
return wrapper
@__calculate
def __add__(self, other):
return self + other
def __radd__(self, other):
return self + other
@__calculate
def __sub__(self, other):
return self - other
@__calculate
def __mul__(self, other):
return self * other
@__calculate
def __floordiv__(self, other):
return self // other
👍
Кажется, что было бы правильнее в методе __add__ поступить также как и с __iadd__. Так, в методе __add__ нам не придется создавать новый экземпляр (мы можем просто увеличить атрибут seconds, а затем вернуть self). Или мне не правильно кажется?)
def __add__(self, other):
if not isinstance(other, (int, Clock)):
raise ArithmeticError("Adding number must be int")
sc = other
if isinstance(other, Clock):
sc = other.seconds
self.seconds += sc
return self
# return Clock(self.seconds + sc)
А Вы прогоните свой вариант на объектах класса например p1 = Class(100), p2 = Class(200) и p3 = p1 + p2, а потом выведите что находится в p1, p2, p3 и поймете почему именно у __iadd__ другая реализация
Дякую за відео, але не дуже зрозумів тут 12:02, що означає sc = other.seconds
Там при сложении операнд справа может быть как число, так и объект Clock. Отсюда и появляется проверка в add.
Вроде неплохо получилось
def universal_operation(self, other, operation: str, for_myself: bool):
if not isinstance(other, (int, Clock)):
raise ArithmeticError(f'unsupported operand type(s) for +: {type(self)} and {type(other)}')
sc = other
if isinstance(other, Clock):
sc = other.seconds
if not for_myself:
if operation == '+':
answer = Clock(self.seconds + sc)
elif operation == '-':
answer = Clock(self.seconds - sc)
return answer
else:
if operation == '+':
self.seconds = (self.seconds + sc) % self.__DAY
elif operation == '-':
self.seconds = (self.seconds - sc) % self.__DAY
return self
а зачем мы в качестве возвращаемого значения метода __add__ устанавливаем НОВЫЙ объект класса Clock с увеличенным количеством секунд:
return Clock(self.seconds + other)
вместо того, чтобы просто увеличить значение для текущего объекта:
return self.seconds + other
?
или это просто демонстрация того, как можно манипулировать данными, а нам на практике уже нужно будет действовать на своё усмотрение в зависимости от ситуации?
@@johnmazepa если нужно несколько слагаемых, например: a+b+c+d
В данной реализации вернется количество секунд, а не экземпляр класса Clock. Если требуется изменить атрибут конкретного класса и вернуть экземпляр этого же класса (без создания нового), предлагаю примерно такой вариант:
def __add__(self, other):
if not isinstance(other, (int, Clock)):
raise ArithmeticError("Атрибут должен быть целым числом (int) и объектом класса Clock")
if isinstance(other, int):
self._seconds += other
if isinstance(other, Clock):
self._seconds += other._seconds
return self
Если передать в seconds bool то TypeError не возникнет
Не понятно как происходит вызов метода radd. Если я выполняю с1 + 100, происходит вызов с1.__add__(100). Вызывая 100 + с1, я ожидаю вызов 100.__add__(c1), т. Е метод класса int. Как интерпретатор понимает, что нужно вызвать именно radd нашего класса?
здесь сам интерпретатор уже "соображает", что нужно вызвать именно radd, т.к. такой метод определен в c1
почему "Магические"?
c1.seconds = c1.seconds + 100. А что, c1.seconds += 100 уже не работает в Python?
за это отвечает магический метод iadd
@selfedu_rus так он под капотом у главного родителя object
Не понятно, если эти методы из коробки идут, то зачем в них еще столько кода писать, почему они сразу в две или одну строчку не идут? или я не про то? )
не про то, можно их вообще не переопределять, если не нужно, в противном случае прописываем свой алгоритм, а сколько строчек кода - это зависит от задачи
При попытке реализовать def __rsub__(self, other):
return self - other
он вычитает из объекта число, хотя хотелось бы наоборот. Как?
Решил проблему простой заменой знака результата. Это нормально, или как-то ещё делают?
Решение задачи поставленной в конце видео: использовать менеджер контекста width? Или это неправильный путь?
не совсем понятно для чего with?
@@selfedu_rus да, глупость я сморозил. Скорее, опять нужна какая-то магия.
Вроде получилось colab.research.google.com/drive/1M4Cdv82HeVBcQE-kn-zTRzjIWFNbrjpt?usp=sharing
попытался превратить магию методов в вуду дескрипторов и замыканий
class Desc:
def __init__(self):
self.opdct={'add':'+','sub':'-','mul':'*','truediv':'/'}
def __set_name__(self,owner,name):
name=name.strip('_')
self.prefix=name[0] if name.startswith(('i','r')) else None
self.name=name[1:] if self.prefix else name
def __get__(self,obj,owner):
def wrapp(other):
if type(other) in (int,float):
n1,n2=obj.num,other
else:
n1,n2=obj.num,other.num
if not self.prefix:
return owner(self.op(self.opdct[self.name],n1,n2))
if self.prefix=='r':
return owner(self.op(self.opdct[self.name],n2,n1))
if self.prefix=='i':
setattr(obj,'num',self.op(self.opdct[self.name],n1,n2))
return obj
return wrapp
@staticmethod
def op(opsign,n1,n2):
return eval(f'{n1}{opsign}{n2}')
class A:
__add__=Desc()
__sub__=Desc()
__mul__=Desc()
__truediv__=Desc()
__radd__=Desc()
__rsub__=Desc()
__rmul__=Desc()
__rtruediv__=Desc()
__iadd__=Desc()
__isub__=Desc()
__imul__=Desc()
__itruediv__=Desc()
def __init__(self,num):
но это все самая первая проба....развитие и улучшение в моих решениях на степике
суть метода использование non-data дескрипторов и замыканий в __get__
типовая конструкция такая
def __get__(self,object,owner):
def inner(other):
return ...
return inner
а для работы с методами такая еще конструкция return owner(getattr(object.param,self.name)(other.param))-если нам например нужно получить результат арифметической операции над объектом как новый объект с параметром -результатом операции над 2 объектами
КОРОЧЕ ПАСАНЫ ,,,,Я ЭТИ ВАШИ МАГМЕТОДЫ НА ДЕСКРИПТОРЕ ВЕРТЕЛ КАК ХОТЕЛ))))))
почему на 23 строчке нельзя было написать
if isinstance(other, Clock):
other = other.seconds
return Clock(self.seconds + other)
без введения доп переменной
правильно я понимаю, что метод __add__ он более общий по сравнению с radd и iadd?
зачем было городить еще и __get_formatted() ? можно было все уместить в f-строку
Очень советую избегать этих магических методов (именно этих 4-х). Лучше сделайте отдельные обычные методы add(), sub() или назовите какими-нибудь increase(), decrease(). И в методах просто соответственно увеличивайте или уменьшайте свойство seconds.
Если же вы все-таки решили добавить логику сложения объектов с этими маг-методами, то обязательно протестируйте результат на всех возможных вариантах: объект справа от плюса, слева от плюса, складываются два объекта и результат присваивается третьему. И обязательно каждый раз проверяйте все свойства (в примере из видео это одно свойство seconds) всех объектов.
А вот если вы создадите дочерний класс от этого, гемор вам обеспечен 😁
Подскажите пожалуйста зачем нужен return self в конце метода __iadd__. буду благодарен
Чтобы результат можно было присвоить какой-либо другой переменной (ссылку на объект). Если этого не требуется, то можно не возвращать (вроде бы).
Меня месяц тревожит вопрос, проиллюстрирую на примере из видео:
c1 = Clock(1000)
c1 = c1 + 100
Как сделать, чтобы работало и c1 = 100 + c1 ? Заранее спасибо)
__radd__
У меня освободилось кучу времени, и прохожу курс в степик. (Но я очень далеко... На 3.7... ((()
Этот курс будет там еще долго?
Уважаю ваш труд. Вы гений.
Спасибо! Да, я надеюсь, курс надолго )
@@selfedu_rus у вас есть курс на степике?
@@ИгорьХамула-х7я по ООП нет
@@ИгорьХамула-х7я есть))
Можно ли в iadd не делать return self, ведь на уже изменили значение атрибута в экземпляре, или будет ошибка?
нужно, т.к. там идет присваивание результата операнду слева от оператора +=
Спасибо за ответ
@@selfedu_rus вряд-ли увижу ответ, но попытаюсь)
В __radd__ мы получаем self и изменяя экземпляр через него, по сути методом и запишем значение, или я не прав? self ведь получается берет как раз что нам и нужно...
Все таки проверил и даже вроде осознал return нужен! ))
Ех зная это можно было бы задание в 3.3 последние легче сделать)))
Как понять незначищий ноль справа , а потом говорите если 1 2 или то будет 0 1 0 2 и тд., где логика? Там помойму число больше нуля вправа едет а нули влево
Да ладно к словам придираться, главное суть же объяснили и код правильно работает. rjust делает выравнивание с право(right).
не думай о секундах свысока.
Урок #14 = Пройден
а __sum__
Материал и подача хорошие, но английский плох. Рекомендую подучить произношение слов, которые используются в программировании. Неправильное произношение вызывает недоумение у незнающих людей. Ещё было бы полезно объяснять полное название методов, например mul - multiply(умножать).
В данном видео метод __add__ читается как "эд" (добавить)
Не мог проще пример взять?!!!! Кучу воды наговорил, пока до маг. метода добрался.
опять бессмысленное усложнение объяснения на ровном месте, с этими секундами, сначала надо понять суть на примитиве, потом рассчитывать космический корабль, часть ресурсов мозга уходит что бы вникнуть ещё и в пример ...