#25. Множественное наследование | Объектно-ориентированное программирование Python
Вставка
- Опубліковано 25 лис 2024
- Курс по Python ООП: stepik.org/a/1...
Как работает множественное наследование (multiple inheritance) в Python. Зачем это нужно. Пример использования при реализации идеи минсинов (mixins). Алгоритм обхода базовых классов MRO.
Плейлист по Python ООП: • Объектно-ориентированн...
Инфо-сайт: proproprogs.ru...
Telegram-канал: t.me/python_se...
как лёгкий пример множественного наследования - молоко(жидкость, еда)
жидкость (вязкость, температура кипения), еда (белки углеводы жиры, калории)
или куртка (одежда, товар)
одежда (размер), товар (цена)
по сути это пересекающиеся круги эйлера
п. с. комент в поддержку видео
Сначала у меня были вопросы, а потом я просто еще раз посмотрела видео... И оказывается все понятно! Вот что значит: "внимательно слушай учителя и будет тебе счастье".
Ещё один пример - класс Datetime, который наследуется от Date и Time. Иногда нужно работать отдельно с датой, иногда со временем, а иногда и с тем, и с другим.
очень интересно и познавательно для меня это наверное самый лучший и интересный урок, так как скоро проект в Джанго буду делать, а там много миксинчиков играет роль, благодарю наставник
Сергей, спасибо за урок! Очень нравится Ваш курс!
Спасибо за труд!
Читал про миксины в 'Книге рецептов' Бизли, Джонс. Довольно специфическая штука. Наверное стоит её отдельно от множественного наследия рассматривать.
Спасибо. Принцип понятен.
Спасибо огромное, Сергей!
👍👍👍
Ух ты продолжение)
перезалив, небольшая ошибка была ))
классный курс
такая полезная инфа, а так мало просмотров, очень жаль
Совсем не мало. Это 24 тысячи учебных просмотров. Даже если это всего 5-10к будущих разработчиков, это уже огромная цифра, которая показывает ценность работы автора. Шутка ли, учитель в школе столько учеников лет за 10 обучает, а тут за год столько. По мне - это очень круто. Тем более тема не первая, это 25-й урок!
Отлично.Спасибо.
Спасибо
Спасибо!
Сергей спасибо огромное за видео! Материал подаете очень доступно и понятно. Не многие могут подать столь сложный материал в доступной форме. Но вопросы все таки есть: когда вы рассказываете о порядке перебора метода init 6:32 используете слайд на котором рисуете стрелки, но Ваши стрелки не совпадают со стрелками слайда. Ваше рассуждения не вызывают вопросов, но тогда получается ошибка в стрелках на слайде?
Спасибо! Да, на слайде - это стрелки структуры множественного наследования классов, а не порядок вызова init(), отсюда и расхождения.
А когда будет продолжение этой темы разговора подробнее ⁉️
Неожиданно
👍
3:14 Сергей (простите меня) - так делать тоже не желательно . Вы весь курс повторяли, что это плохой тон и подобное следует реализовывать через определение класса (либо вводить метод класса для обработки ID).
Все на уровне рекомендаций и чтобы не сильно усложнять текст программы, иногда можно отходить от этих рекомендаций.
👍👍👍👍👍
Вы это видео выложили в общий доступ. Он же платный, по ссылке только должен работать)
В курсах самое ценное - это задания и их проверка, т.е. практика. Все остальное должно быть бесплатным.
@@selfedu_rus понял, у Вас весь курс просто ниже, а это перезалив ролика. Спасибо!
Сергей доброго дня, какие мысли у вас о том что о наследовании говорят как о плохом примере использования при построении архитектуры, можете рассказать о паттернах в вашем видении!
Без наследования нет полноценного ООП - это могу определенно сказать. Другое дело, все в меру. Не нужно "лепить" слишком много базовых классов. Как всегда должна быть золотая середина.
С паттернами немного знакомлю на курсе по Python ООП. Возможно, в будущем расширю этот раздел.
Спасибо за урок, подскажите как через super() вызвать метод print_info() именно из Mixinlog?
для надежности придется написать Mixinlog.print_info() но классы нужно проектировать так, чтобы этого не требовалось
Сергей, здравствуйте. Такой вопрос: как так выходит, что порядок MRO выводит: Notebook, Goods, MixinLog, MixinLog2, если у вас в примере видел, что инициализатор Goods выводит сообщение последним? Какое-то несоответствие тому, что выводит инициализатор и порядок MRO.
Порядок mro будет таким каким вы его написали. Что касается порядка выводов print, то вы сперва делегируете вызов функции на следующий класс в этой иерархии, а только потом уже инициализируете свои атрибуты и вызываете print в класса Goods, тоесть вы спервы вызываете super().__init__, там отрабатывает свой print, который находистя в инцииализаторе класса MaxinLog, и только потом выводится print класса Goods.
@@rukigaki 😝 . Уже разобрался, спасибо 😉
очень странно, у меня почему-то save_cell_log сразу сработал. может это как-то от версии зависит?
получается можно сказать, что миксины при необходимости, расширяют функционал базового класса? допустим, на основе базы создано множество дочерних, а к только к некоторым из низ нужно добавить доп методы, тогда к этим класса добавляют наследование от определенного миксина
Микмины, скорее, как кирпичики, из которых можно конфигурировать функциональность дочерних классов.
@@selfedu_rus благодарю)
Здравствуйте, возник один вопрос : не получится ли для последней цели из видео написать что-то вроде super().super().print_info()? Просто интересно. Спасибо за видео!
Думаю, не лучшая конструкция, т.к. super() просматривает базовые классы по списку MRO, и это может легко привести к ошибке.
Ещё такой вопрос, как вы делаете несколько строк комментарием за одно нажатие? Если выделить все и нажать решётку то оно просто заменяется у меня. Заранее спасибо
@@Cygni7 Сочетание клавиш ctrl + /
А верно, что метод super() всегда возвращает вспомогательный класс для работы методов класса, идущего вторым в коллекции __mro__?
super - это класс, поэтому super() - формирование специального объекта-посредника, через который и происходит обращение к базовому классу в соответствии с порядком коллекции __mro__
5:39 - и все-таки Синьору пришлось лезть руками в базовый класс и вписывать в строку №3 код "инициализатора родительского класса"
Это допустимая погрешность в случаях, когда "Синьора просят добавить функционал без вмешательства в базовый класс"?
это нарушение OCP
А чем такая плоха реализация
class CLS_A:
def __init__(self, atr_a):
self.str_a = 'STR_AAA :' + atr_a
class CLS_B:
def __init__(self, atr_b):
self.str_b = 'STR_BBB: ' + atr_b
class CLS_C(CLS_A, CLS_B):
def __init__(self, atr_a, atr_b):
super().__init__(atr_a)
CLS_B.__init__(self, atr_b)
exmpl = CLS_C('клас a', 'класс b')
print(exmpl.str_a, exmpl.str_b)
а что если в конструкторе основного базового класса принимать ещё и kwargs, а потом их передавать в конструкторы всех следующих классов, тогда по идее каждый будет брать свои аргументы и порядок не будет иметь значение
можно, но лучше вообще без аргументов )) все равно кто то в будущем захочет внести изменения и это станет проблемой для всего проекта
Музыка будто из гравити фолз в конце)
почему нельзя в __init__ конечного класса наследника, просто не вызвать через имена классов родителей методы __init__ в нужном порядке и нужными параметрами?
путаница получится, т.к. скоро забудете, кто из кого вызывается и редактировать это будет очень сложно
@@selfedu_rus вопрос очень спорный, т. к. у нас может быть много классов предков, каждый со своими параметрами (рассматриваю наихудший вариант). в результате можно быстрее запутаться перетаскивая параметры через функцию super().__init__(). Но, все равно спасибо!
@@СарматПересветов это уже к вопросу проектирования программного кода. Желательно такого избегать (по возможности).
@@selfedu_rus Посмотрел все ваши предыдущие видео по ООП и там все четко и понятно, а при просмотре этого видео все мое понимание ООП выставило штыки и отказалось принимать информацию. Тут нарушены все принципы ООП. один родительский класс ссылается на другой . Плюс для для создания дочернего класса мы изменяем родительский. А если он из какой-нить библиотеки? Я тоже считаю что вызовы __init__ возможны только из дочернего класса без изменений родительского. Специально пошел искать эту инфу у других авторов. Егоров, в своем курсе, вызывает все из дочернего . Не смотря на это видео, остальной курс, из того что я успел посмотреть, супер. Спасибо! Продолжаю изучать ООП
Сергей, а зачем у класса Miksinlog2 есть функция super , если этот класс находится в конце по наследованию и в свою очередь не вызывает никаких инициализаторов?
Это для общности, если появится класс Miksin3 или порядок в наследовании поменяется.
@@selfedu_rus спасибо
11:02 не лутше писать super().__init__(*args,**kwargs) ?
А ты откуда вы их возьмете? Эти параметры используются при инициализации метода, вы не сможете послать их в аргументе уже готового метода. Чтобы использовать их в качестве аргумента, вам надо явно где-то в классе этот кортеж(список, множество) args и словарь kwargs определить, только зачем? Более того, первым элементом кортежа должна быть ссылка на объект)
Видимо действительно сложно применимый функционал
2:26 - интернет магазин по продаже товаров - десятки тысяч дочерних классов товаров - и тут наш "хороший senior" решил применить идею миксинов😅
поменьше бы таких "хороший senior" разработчиков))
Чем то похоже на декораторы.
Урок #25 = Пройден
Все-же Пито - это уродский язык. Автор, поначалу, хотел быть оригинальным, затем четверть века исправляет собственную дурь.
Приветствую вас, можете пожалуйста указать на ошибку в моем коде
import datetime
class Merchendise:
def __init__(self, name_thing, wight, price):
super().__init__()
self.name_thing = name_thing
self.wight = wight
self.price = price
def mech_method(self):
return f'{self.name_thing}, {self.wight}, {self.price}'
class Thing(Merchendise):
thing = 0
def thing_method(self):
super().__init__(self.name_thing, self.wight, self.price)
self.thing += 1
price = self.thing * self.price
return price
class Time(Thing):
date_time = datetime.datetime(year=2023, month=12, day=12)
def __init__(self, date_time):
super().__init__(self.name_thing, self.wight, self.price)
self.date_time = date_time
def time_method(self):
self.thing_method()
time = self.date_time + datetime.timedelta(days=self.thing)
return time
class ElectroGuitar(Merchendise, Thing, Time):
pass
eg = ElectroGuitar('Ibanez', 1.5, 220000)
print(eg.time_method())
print(eg.thing_method())
print(eg.mech_method())
#ошибка
class ElectroGuitar(Merchendise, Thing, Time):
TypeError: Cannot create a consistent method resolution
order (MRO) for bases Merchendise, Thing, Time
Потому что алгоритм MRO должен соблюдать детерминированный порядок поиска атрибутов в иерархии наследования. Но в вашем случае он сталкивается с явным противоречием.
Смотрите сами:
_ElectroGuitar -> Merchendise -> _*_Thing_*_ -> Time -> _*_Thing_*_ -> ..._
Дальше даже нет смысла рассматривать, так как уже видно, что в цепочке один из классов повторился несколько раз, что в свою очередь неизбежно ведет к ситуации, когда родитель окажется впереди своего потомка, а такового в этом алгоритме быть не должно!