Сегодня поговорим о способах настройки "правдивости" объектов класса 🤔
Когда к экземпляру класса применяется функция bool() - явно или неявно - она определяет, считается ли объект "истинным" или "ложным". Напомню: для пустых данных функция выдаёт False, для непустых - True.
Где это может пригодиться? Ваши варианты?
Например, в программах где нужны кастомные проверки истинности объектов ✅
Для настройки этого поведения используют:
-
-
По умолчанию bool() ВСЕГДА возвращает True для любых экземпляров кастомного класса. Зачем тогда это нужно? 😅
А вот зачем - можно переопределить через магические методы!
Приоритет проверки:
1. Сначала ищется
2. Если его нет -
3. Если и его нет - всегда True
Всё просто! 🎯
Перейдем к практике:
Пусть есть такой простой класс Point:
модифицируем len()
изменяем bool()
Важный нюанс!
Теперь можно использовать в условных операторах: (bool() вызывается неявно)
Как вам такие "магические" методы? 👇
🔥 — Огонь, приму на вооружение!
🤔 — Знал, но освежил память.
💬 — Есть вопросы в комментарии!
Когда к экземпляру класса применяется функция bool() - явно или неявно - она определяет, считается ли объект "истинным" или "ложным". Напомню: для пустых данных функция выдаёт False, для непустых - True.
Где это может пригодиться? Ваши варианты?
Например, в программах где нужны кастомные проверки истинности объектов ✅
Для настройки этого поведения используют:
-
__bool__ - основной способ-
__len__ - запасной вариантПо умолчанию bool() ВСЕГДА возвращает True для любых экземпляров кастомного класса. Зачем тогда это нужно? 😅
А вот зачем - можно переопределить через магические методы!
Приоритет проверки:
1. Сначала ищется
__bool__2. Если его нет -
__len__() 3. Если и его нет - всегда True
Всё просто! 🎯
Перейдем к практике:
Пусть есть такой простой класс Point:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
модифицируем len()
def __len__(self):
print('__len__')
return self.x**2 + self.y**2
изменяем bool()
def __bool__(self):
print('__bool__')
return self.x == self.y
Важный нюанс!
__bool__ ОБЯЗАН возвращать True или False ❗️Теперь можно использовать в условных операторах: (bool() вызывается неявно)
p = Point(2, 2)
if p:
print("объект p дает True") # сработает это
else:
print("объект p дает False") # сработает это, если
Как вам такие "магические" методы? 👇
🔥 — Огонь, приму на вооружение!
🤔 — Знал, но освежил память.
💬 — Есть вопросы в комментарии!
🔥8
Поехали разбираться с магией индексации: сделаем классы "как список" 🎩✨
Пусть есть такой класс студент, создадим его экземпляр
Думаю понятно как обращаться к элементам списка marks: st1.marks[2]
А если хочется напрямую? st1[2]. Перехочется😂 - очевидно получим ошибку.❌
Это можем поправить, добавив метод
Добавим метод
Думаю уже догадались какой остался и для чего:
Можно в эти методы дописывать необходимые вам проверки, чтобы как было задумано использовать пользовательский класс. Но и без этого будут вылезать ошибки, при неверном использовании, т.к. мы (в данном случае) будем уже работать со стандартными типами данных, в частности со списком.
Приведу пару примеров модернизации (смотреть в комменты)
Как вам такие "магические" методы? 👇
🔥 — Огонь, приму на вооружение!
🤔 — Знал, но освежил память.
💬 — Есть вопросы в комментарии!
Пусть есть такой класс студент, создадим его экземпляр
class Student:
def __init__(self, name, marks):
self.name = name
self.marks = list(marks)
st1 = Student('Евгений', [5, 5, 3, 2, 5])
Думаю понятно как обращаться к элементам списка marks: st1.marks[2]
А если хочется напрямую? st1[2]. Перехочется😂 - очевидно получим ошибку.❌
Это можем поправить, добавив метод
__getitem__' - для получения значений.def __getitem__(self, item): # item - ключ/индекс
return self.marks[item]
Добавим метод
__setitem__ - для изменения значений, например: по типу st1[2] = 4,def __setitem__(self, key, value): # key - индекс, value - # значение
self.marks[key] = value
Думаю уже догадались какой остался и для чего:
__delitem__' - для удаления значенийdef __delitem__(self, key): # key - индекс
self.marks.pop(key)
Можно в эти методы дописывать необходимые вам проверки, чтобы как было задумано использовать пользовательский класс. Но и без этого будут вылезать ошибки, при неверном использовании, т.к. мы (в данном случае) будем уже работать со стандартными типами данных, в частности со списком.
Приведу пару примеров модернизации (смотреть в комменты)
Как вам такие "магические" методы? 👇
🔥 — Огонь, приму на вооружение!
🤔 — Знал, но освежил память.
💬 — Есть вопросы в комментарии!
🔥6
Слепая печать для программиста: 4 ключевые причины
1) ⚡️Скорость разработки: Быстрая печать = быстрое воплощение идей в код, что напрямую ускоряет реализацию проекта и поддерживает высокий темп работы.
2) 🎯 Фокус на логике: Навык позволяет перенести все ментальное внимание с механики ввода на решение сложных задач и проектирование алгоритмов.
3) 🧠 Снижение усталости: Автоматизация печати минимизирует умственное напряжение и физическое утомление глаз, позволяя дольше сохранять высокую концентрацию.
4) ✅ Повышение качества кода: Печать вслепую позволяет мгновенно обнаруживать и исправлять опечатки и синтаксические ошибки, так как взгляд фиксируется на экране.
Резюмируя: время, потраченное на освоение слепой печати (это занимает в среднем 2-4 недели регулярных занятий), — это лучшая инвестиция в себя, точка! 🚀
Я сам прошел этот путь, и могу подтвердить: результат того стоит. Этот навык не приходит сам собой, нужна практика, но эффект ошеломляющий. 💪
Начните практиковаться уже сейчас, и через 3 месяца вы будете печатать вслепую уверенно. Вы сэкономите сотни, а скорее тысячи рабочих часов за свою карьеру! ⏳
В следующем посте я поделюсь списком лучших тренажеров, которые я лично использовал, и расскажу, где лучше всего учиться. Готовьтесь к бусту продуктивности! 🔥😈
А вы как считаете, нужный это навык или все-таки нет?🙃
1) ⚡️Скорость разработки: Быстрая печать = быстрое воплощение идей в код, что напрямую ускоряет реализацию проекта и поддерживает высокий темп работы.
2) 🎯 Фокус на логике: Навык позволяет перенести все ментальное внимание с механики ввода на решение сложных задач и проектирование алгоритмов.
3) 🧠 Снижение усталости: Автоматизация печати минимизирует умственное напряжение и физическое утомление глаз, позволяя дольше сохранять высокую концентрацию.
4) ✅ Повышение качества кода: Печать вслепую позволяет мгновенно обнаруживать и исправлять опечатки и синтаксические ошибки, так как взгляд фиксируется на экране.
Резюмируя: время, потраченное на освоение слепой печати (это занимает в среднем 2-4 недели регулярных занятий), — это лучшая инвестиция в себя, точка! 🚀
Я сам прошел этот путь, и могу подтвердить: результат того стоит. Этот навык не приходит сам собой, нужна практика, но эффект ошеломляющий. 💪
Начните практиковаться уже сейчас, и через 3 месяца вы будете печатать вслепую уверенно. Вы сэкономите сотни, а скорее тысячи рабочих часов за свою карьеру! ⏳
В следующем посте я поделюсь списком лучших тренажеров, которые я лично использовал, и расскажу, где лучше всего учиться. Готовьтесь к бусту продуктивности! 🔥😈
А вы как считаете, нужный это навык или все-таки нет?🙃
🔥9❤1👍1
Хотите так же? Смотрите ниже🙃
Печатать вслепую я начал давно, пришлось этому обучиться в школе, чтобы не отставать от других😬В подробности углубляться не буду, вот что мне помогло:
Для изучения техники печати:
• Тренажер Stamina: Проходил еще на sega с клавиатурой). Это изначально поставило мне верно пальцы и дало первый буст в печати. Кстати, если читать текст, там очень забавные фразы предлагают😁
• Соло на клавиатуре: После полного прохождения вы будете ненавидеть или любить фамилию Шахиджанян, но уж точно запомните :D). А если серьёзно, то считаю, что это основа основ для слепой 10-ти пальцевой печати!
• Codewar: Нет, это не про решение задач😄Относительно новый сайт. Завлекло то, что дает персональные рекомендации по улучшению и гемфикация процесса (но на любителя).
Для практики точности и скорости:
• Клавогонки: Куча разных текстов
• Monkeytype: Аналогично гонкам, но на англ.
• TypingClub: Очень много упражнений для eng. lang.
• Еще один сайт: Здесь ссылка именно на тренажер печати на Python.
Печатать вслепую я начал давно, пришлось этому обучиться в школе, чтобы не отставать от других😬В подробности углубляться не буду, вот что мне помогло:
Для изучения техники печати:
• Тренажер Stamina: Проходил еще на sega с клавиатурой). Это изначально поставило мне верно пальцы и дало первый буст в печати. Кстати, если читать текст, там очень забавные фразы предлагают😁
• Соло на клавиатуре: После полного прохождения вы будете ненавидеть или любить фамилию Шахиджанян, но уж точно запомните :D). А если серьёзно, то считаю, что это основа основ для слепой 10-ти пальцевой печати!
• Codewar: Нет, это не про решение задач😄Относительно новый сайт. Завлекло то, что дает персональные рекомендации по улучшению и гемфикация процесса (но на любителя).
Для практики точности и скорости:
• Клавогонки: Куча разных текстов
• Monkeytype: Аналогично гонкам, но на англ.
• TypingClub: Очень много упражнений для eng. lang.
• Еще один сайт: Здесь ссылка именно на тренажер печати на Python.
🔥11❤🔥2⚡1🤝1
🔄 Итераторы в Python: "магия"
Давайте разберемся, как работают итераторы в классах! Начнем с основы:
Для любого итерируемого объекта (например, список) мы можем создать итератор → с помощью next() читаем элементы последовательно → когда доходим до конца, получаем StopIteration
Пример с range():
Работает аналогично! 🔄 Вспомнили?
Тогда переходим к классам! 🚀
Честно: сам не очень люблю такие темы (наверно, потому что забываю что-то 😅), но знать НАДО!
Напомнило мне про боль в односвязных и двусвязных списках — уже решил штук 5-6 задач по ним за последнюю главу. Теперь выглядят не так страшно! 💪
Поехали разбираться! Создаем свой итератор — класс FRange (аналог range() для вещественных чисел):
Добавляем метод для получения значений:
Используем так (записи эквивалентны):
Важный момент! 🤔
Так происходит потому что функция next() неявно вызывает магический метод `__next.__` Соответственно, получаем то значение, которое возвращает метод, а объект fr выступает тут в роли итератора (объект, у которого есть магический метод next(), но и не только он).
Пробуем через for:
Получаем: TypeError: 'FRange' object is not iterable ❌
Почему? Не можем создать итератор для fr (не можем вызвать iter()).
Исправляем — добавляем
Нюанс: инициализацию self.value можно перенести в
Итог: двумя методами (
В комментах выложу еще один пример - продложение этого.
Как вам такие "магические" методы? 👇
🔥 — Огонь, приму на вооружение!
🤔 — Знал, но освежил память.
💬 — Есть вопросы - в комментарии!
P.S Если что-то не так написал по теории - поправьте)
Задача:, вам необходимо разработать итератор, который бы перебирал указанные столбцы двумерного списка. Список представляет собой двумерную таблицу из данных:
Для этого в программе необходимо объявить класс с именем IterColumn, объекты которого создаются командой:
где lst - ссылка на двумерный список; column - индекс перебираемого столбца (отсчитывается от 0).
Затем, с объектами класса IterColumn должны быть доступны следующие операции:
Решение:
def __iter__(self):
for row in self.__tables:
yield ( elem.data for elem in row)
__iter__ и __next__Давайте разберемся, как работают итераторы в классах! Начнем с основы:
Для любого итерируемого объекта (например, список) мы можем создать итератор → с помощью next() читаем элементы последовательно → когда доходим до конца, получаем StopIteration
Пример с range():
a = iter(range(5))
print(next(a)) # 0
Работает аналогично! 🔄 Вспомнили?
Тогда переходим к классам! 🚀
Честно: сам не очень люблю такие темы (наверно, потому что забываю что-то 😅), но знать НАДО!
Напомнило мне про боль в односвязных и двусвязных списках — уже решил штук 5-6 задач по ним за последнюю главу. Теперь выглядят не так страшно! 💪
Поехали разбираться! Создаем свой итератор — класс FRange (аналог range() для вещественных чисел):
class FRange:
def __init__(self, start=0.0, stop=0.0, step=1.0):
self.start = start
self.stop = stop
self.step = step
Добавляем метод для получения значений:
def __next__(self):
if self.value + self.step < self.stop:
self.value += self.step
return self.value
raise StopIteration('конец') # 🛑
Используем так (записи эквивалентны):
fr = FRange(0, 2, 0.5)
print(next(fr)) # 0.0
print(fr.__next__()) # 0.5
Важный момент! 🤔
Так происходит потому что функция next() неявно вызывает магический метод `__next.__` Соответственно, получаем то значение, которое возвращает метод, а объект fr выступает тут в роли итератора (объект, у которого есть магический метод next(), но и не только он).
Пробуем через for:
for x in fr:
print(x)
Получаем: TypeError: 'FRange' object is not iterable ❌
Почему? Не можем создать итератор для fr (не можем вызвать iter()).
Исправляем — добавляем
__iter__:def __iter__(self):
# self.value = self.start - self.step
return self
Нюанс: инициализацию self.value можно перенести в
__iter__, но тогда print(next(fr)) сломается — не найдет self.value!Итог: двумя методами (
__iter__ и __next__) превратили класс в итерируемый объект + "сказали" как надо формировать очередное значение для этого итерируемого объекта. 🎉В комментах выложу еще один пример - продложение этого.
Как вам такие "магические" методы? 👇
🔥 — Огонь, приму на вооружение!
🤔 — Знал, но освежил память.
💬 — Есть вопросы - в комментарии!
P.S Если что-то не так написал по теории - поправьте)
Еще пример для себя - всё равно это никто не прочитает уже))
Задача:, вам необходимо разработать итератор, который бы перебирал указанные столбцы двумерного списка. Список представляет собой двумерную таблицу из данных:
lst = [[x11, x12, ..., x1N],
[x21, x22, ..., x2N],
...
[xM1, xM2, ..., xMN]
]
Для этого в программе необходимо объявить класс с именем IterColumn, объекты которого создаются командой:
it = IterColumn(lst, column)
где lst - ссылка на двумерный список; column - индекс перебираемого столбца (отсчитывается от 0).
Затем, с объектами класса IterColumn должны быть доступны следующие операции:
it = IterColumn(lst, 1)
for x in it: # последовательный перебор всех элементов в столбцах списка: x12, x22, ..., xM2
print(x)
it_iter = iter(it)
x = next(it_iter)
Решение:
class IterColumn:
def __init__(self, lst, index_column):
self.lst = lst[:]
self.index_column = index_column
self.result = [self.lst[i][self.index_column] for i in range(len(self.lst))]
def __iter__(self): # iter как делать, вариант
for elem in self.result:
yield elem
for row in self.__tables:
yield (
🔥2🤔2
Вчера утром с телефона зашёл на Степик. Вижу такую белиберду😑
Сначала ничего не понял, подумал вообще что сплю😂 Как оказалось - нет, даже ущипнул себя, для проверки как в фильмах😁 Я вообще без понятия как описание профиля превратилось в такое и как долго это уже у меня😐
Тут вижу два варианта: либо я ооооочень удачно уснул с телефоном на лице, либо платформа решила надо мной порофлить(теорию заговора никто не отменял)😑
Сначала ничего не понял, подумал вообще что сплю😂 Как оказалось - нет, даже ущипнул себя, для проверки как в фильмах😁 Я вообще без понятия как описание профиля превратилось в такое и как долго это уже у меня😐
Тут вижу два варианта: либо я ооооочень удачно уснул с телефоном на лице, либо платформа решила надо мной порофлить(теорию заговора никто не отменял)😑
😁5
Заход на ООП немного затянулся🐢
Начал за здравие, а заканчиваю... как говорится.., ну вы поняли😅
Но вот уже добрался до конца очередной главы. Ожидает последнее задание — очередное «испытание», на этот раз "магическое". Ещё даже не открывал, а уже чувствую "лёгкий" трепет ✨
Дело в том, что в этом курсе сложность задач можно измерять в минутах видеоразбора:
• Совсем простые — разбора нет 😊
• Лёгкие — видео до 6 минут 🧐
• Средние — 8-10 минут 😐
• Сложные — от 12+ 😰
Но раз на раз не приходится, могу лёгкую задачу решать пару часов, а сложная улетает меньше чем пол часа. Но чаще всего длина видео коррелирует со сложностью задачи.
И вот... Разбор ЭТОЙ задачи — 25+ минут. 🫣
Что же там такое должно быть, если преподаватель её почти полчаса разжёвывает? Моя фантазия рисует суровые картины.😐
Если я внезапно пропаду — вы знаете, где меня искать. Я ушёл в глубокий медитативный транс общения с Питоном. 🧘♂️💻
А у вас как дела с ООП? Давно прошли, сейчас штурмуете или путаетесь в трёх соснах?👇
Начал за здравие, а заканчиваю... как говорится.., ну вы поняли😅
Но вот уже добрался до конца очередной главы. Ожидает последнее задание — очередное «испытание», на этот раз "магическое". Ещё даже не открывал, а уже чувствую "лёгкий" трепет ✨
Дело в том, что в этом курсе сложность задач можно измерять в минутах видеоразбора:
• Совсем простые — разбора нет 😊
• Лёгкие — видео до 6 минут 🧐
• Средние — 8-10 минут 😐
• Сложные — от 12+ 😰
Но раз на раз не приходится, могу лёгкую задачу решать пару часов, а сложная улетает меньше чем пол часа. Но чаще всего длина видео коррелирует со сложностью задачи.
И вот... Разбор ЭТОЙ задачи — 25+ минут. 🫣
Что же там такое должно быть, если преподаватель её почти полчаса разжёвывает? Моя фантазия рисует суровые картины.😐
Если я внезапно пропаду — вы знаете, где меня искать. Я ушёл в глубокий медитативный транс общения с Питоном. 🧘♂️💻
А у вас как дела с ООП? Давно прошли, сейчас штурмуете или путаетесь в трёх соснах?👇
🔥2❤1
Все-таки этот "зверь" был не так страшен=)😅
Задание было написать игру крестики-нолики в ООП-шном стиле. Не знаю сколько точно времени заняло, но где-то 2 до 6 часов.⌛️ Мне кажется, если бы писал все через функции и взял например список для игрового поля, то было бы быстрее сделано. Но ничего... Надо учиться мыслить и немного в другом формате.
Первый раз сдаю, и не понимаю почему не принимает решение. Хотя в Pycharm всё работает и логика верна🤔 Оказалось банальная причина - опять немного не так прочитал. Такой косяк у меня частенько бывает😄 В общем, не заметил что is_human_win, is_computer_win, is_draw - это объекты-свойства(@property).
Изначально реализовал их как методы, и думаю чё не проходит то решение, всё вроде правильно.🤷♂️ Как только это исправил - всё заработало. Конечно не с 1 раза, (хотя можно было и с первого) просто достало, что assert-ы не проходило, и кидал после каждой мелкой поправки)) Хотя всё честно, логика то у программы была другая).
Вот так это выглядит:
🟦🟦🟦
🟦❌🟦
🟦⭕️🟦
Плюс в консоле подсказки и описания по игре🎮 В коментарий скину код. Можете попробоваться сразиться с компьютером и попытаться выиграть. Даже близко на ИИ не тянет)😂 Ну правильно, не на алгоритмы ж была задача. А продумывать код на 255 168 возможных партий, буду как-нибудь в другой раз)
Задание было написать игру крестики-нолики в ООП-шном стиле. Не знаю сколько точно времени заняло, но где-то 2 до 6 часов.⌛️ Мне кажется, если бы писал все через функции и взял например список для игрового поля, то было бы быстрее сделано. Но ничего... Надо учиться мыслить и немного в другом формате.
Первый раз сдаю, и не понимаю почему не принимает решение. Хотя в Pycharm всё работает и логика верна🤔 Оказалось банальная причина - опять немного не так прочитал. Такой косяк у меня частенько бывает😄 В общем, не заметил что is_human_win, is_computer_win, is_draw - это объекты-свойства(@property).
Изначально реализовал их как методы, и думаю чё не проходит то решение, всё вроде правильно.🤷♂️ Как только это исправил - всё заработало. Конечно не с 1 раза, (хотя можно было и с первого) просто достало, что assert-ы не проходило, и кидал после каждой мелкой поправки)) Хотя всё честно, логика то у программы была другая).
Вот так это выглядит:
🟦🟦🟦
🟦❌🟦
🟦⭕️🟦
Плюс в консоле подсказки и описания по игре🎮 В коментарий скину код. Можете попробоваться сразиться с компьютером и попытаться выиграть. Даже близко на ИИ не тянет)😂 Ну правильно, не на алгоритмы ж была задача. А продумывать код на 255 168 возможных партий, буду как-нибудь в другой раз)
🔥5❤2
Добрался до наследования.
Пусть есть два класса. Очевидно что артибуты одного из другого мы никак не можем получить при такой схеме реализации. У них разное пространство имён.
Но можем установить связь между ними, например, чтобы открытые атрибуты Geom были доступны в классе Line :
Теперь можем получить
Что это такое - понятно. Теперь для чего это нужно - разберемся на примере.
Добавим метод для установки координат в класс Line
А затем создадим еще одну фигуру, допустим прямоугольник Rect, и у него тоже будет метод установки координат. Но тогда получится дублирование кода. И метод выше мы можем вынести(определить) в базовом классе Geom.
Почему так работает? Ведь конкретно данного метода set_coords(self) нет в дочерних класса(Rect, Line). Но когда интерпретатор его там не находит, то продолжает искать у родителей. Причем 'self' из этого метода будет ссылаться не на объект базового класса, а на тот экземпляр(объект) из которого он был вызван! Это важный момент.(картинку с пояснением увидите в комментариях). Всё зависит от того откуда метод set_coords() был вызван.
Почему это важно? Например, здесь self формирует локальные свойства/артибуты для дочерних классов Line, Rect.
Немножко вредных советов от НеУспенского:
Создали так(обратите внимание на последнюю строчку)
Запустили (ниже):
Сработает или ошибка из-за отсутствия метода draw() в базовом классе?🧐А если set_coords() вызовем через объект класса Geom?🧐
Все тоже самое применимо при обращении к свойствам базового класса.
Можем там же переопределять атрибуты/методы в дочерних классах, например: 👇
Это очень похоже на систему поиска атрибутов/методов у объекта класса vs самого класса. Сначала ищется у экземпляра, а если не находит, то ищет в классе. Самые базовые понятие, если с ними разобрались, то и наследование пойдёт тоже легко (проверю))). Тоже самое в наследовании, сначала ищет в текущем классе, а потом в родительских, поднимаясь вверх по иерархии.
Пример наследования init() в дочернем классе:
Оставляйте фидбек по материалу 👇
🔥 — Огонь, приму на вооружение!
🤔 — Знал, но освежил память.
💬 — Есть вопросы в комментарии!
class Geom:
name = 'Geom'
class Line:
def draw(self):
print('Рисование линии')
g = Geom()
l = Line()
Пусть есть два класса. Очевидно что артибуты одного из другого мы никак не можем получить при такой схеме реализации. У них разное пространство имён.
Но можем установить связь между ними, например, чтобы открытые атрибуты Geom были доступны в классе Line :
class Line(Geom):
def draw(self):
print('Рисование линии')
Теперь можем получить
l.name # GeomНаследование - когда один класс определяется на основе другого. Причем, класс Geom называется родительским или базовым, а класс Line – подклассом родительского или дочерним классом.
Что это такое - понятно. Теперь для чего это нужно - разберемся на примере.
Добавим метод для установки координат в класс Line
def set_coords(self, x1, y1, x2, y2):
self.x1 = x1
self.y1 = y1
self.x2 = x2
self.y2 = y2
А затем создадим еще одну фигуру, допустим прямоугольник Rect, и у него тоже будет метод установки координат. Но тогда получится дублирование кода. И метод выше мы можем вынести(определить) в базовом классе Geom.
Почему так работает? Ведь конкретно данного метода set_coords(self) нет в дочерних класса(Rect, Line). Но когда интерпретатор его там не находит, то продолжает искать у родителей. Причем 'self' из этого метода будет ссылаться не на объект базового класса, а на тот экземпляр(объект) из которого он был вызван! Это важный момент.(картинку с пояснением увидите в комментариях). Всё зависит от того откуда метод set_coords() был вызван.
Почему это важно? Например, здесь self формирует локальные свойства/артибуты для дочерних классов Line, Rect.
Немножко вредных советов от НеУспенского:
Создали так(обратите внимание на последнюю строчку)
class Geom:
name = 'Geom'
def set_coords(self, x1, y1, x2, y2):
self.x1 = x1
self.y1 = y1
self.x2 = x2
self.y2 = y2
self.draw()
Запустили (ниже):
l = Line()
r = Rect()
l.set_coords(1, 1, 2 , 2)
r.set_coords(1, 1, 2 , 2)
Сработает или ошибка из-за отсутствия метода draw() в базовом классе?🧐А если set_coords() вызовем через объект класса Geom?🧐
Делаем вывод: При реализации методов в классах следует придерживаться одного простого правила: внутри них обращаться только к разрешенным атрибутам либо внутри текущего класса, либо базовых классов. Но не дочерних.
Все тоже самое применимо при обращении к свойствам базового класса.
Можем там же переопределять атрибуты/методы в дочерних классах, например: 👇
class Line(Geom):
name = 'Line'
Это очень похоже на систему поиска атрибутов/методов у объекта класса vs самого класса. Сначала ищется у экземпляра, а если не находит, то ищет в классе. Самые базовые понятие, если с ними разобрались, то и наследование пойдёт тоже легко (проверю))). Тоже самое в наследовании, сначала ищет в текущем классе, а потом в родительских, поднимаясь вверх по иерархии.
Пример наследования init() в дочернем классе:
class Table:
def __init__(self, model, color):
self.model = model
self.color = color
class RoundTable(Table): # пример для реализации наследования
def __init__(self, model, color, radius, height):
# super() здесь вызывает "старый" инит и устанавливает переданные атрибуты (обычный вызов функции)
super().__init__(model, color)
self.radius = radius
self.height = height
Оставляйте фидбек по материалу 👇
🔥 — Огонь, приму на вооружение!
🤔 — Знал, но освежил память.
💬 — Есть вопросы в комментарии!
🤔2
✅Викторина. Что произойдёт после запуска программы ?
Cмотрите в комментарий под постом
Cмотрите в комментарий под постом
Anonymous Quiz
10%
Ошибка при создании из-за 1 элемента
48%
Ошибка при создании из-за 3 элемента
28%
Выполнится в штатном режиме
3%
Длина словаря будет строго меньше 3
10%
Какая еще длина словаря? Иди учи мат. часть.
🤔5
❄️ Всех с первым днём зимы!
Ну или у кого что там за окном😁У нас в городе пока вот так👆
Кажется, что снега в этом году могу вообще не увидеть.
А если и будет, то выпадет так быстро, что просплю единственный снегопад))
Как у вас настроение? Чувствуете уже новогодний вайб?🎄
Успеваете доделать то, что планировали на 2025 год?
И ещё вопрос из любопытства: а откуда вы?
Нас тут три с половиной сотни набралось за полгода — интересно, из каких городов и даже стран люди в этом канале!
Где-то, кажется в курсе ПП, видел интерактивную карту поселений участников, мб тоже такое сделать🙃
Рассказывайте в комментах — и про зиму за окном, и про планы, и про ваш город 😊
Ну или у кого что там за окном😁У нас в городе пока вот так👆
Кажется, что снега в этом году могу вообще не увидеть.
А если и будет, то выпадет так быстро, что просплю единственный снегопад))
Как у вас настроение? Чувствуете уже новогодний вайб?🎄
Успеваете доделать то, что планировали на 2025 год?
И ещё вопрос из любопытства: а откуда вы?
Нас тут три с половиной сотни набралось за полгода — интересно, из каких городов и даже стран люди в этом канале!
Где-то, кажется в курсе ПП, видел интерактивную карту поселений участников, мб тоже такое сделать🙃
Рассказывайте в комментах — и про зиму за окном, и про планы, и про ваш город 😊
❤5
issubclass() 🔍 Наследование от встроенных типов
Продолжаем сложные, но интересные объяснения! 😈
📌 Пользовательский класс по умолчанию наследуется от базового класса object.
Зачем? 🤔 Чтобы обеспечить базовый функционал работы с классами!
💡Например: вот эта "абракадабра" с адресом объекта при выводе - это результат работы методa
Атрибут
Три способа проверки на принадлежность класса/объекта к классу:
🔧 С помощью функции issubclass(дочерний_класс, базовый_класс) можем определять,
является ли один класс подклассом другого (именно класс, а не его объекты).
🔧 А вот isinstance(объект, класс) тоже учитывает наследование! 🎯
Эта функция проверяет, является ли объект экземпляром класса или экземпляром любого из его подклассов!
🔧 Есть еще похожая функция type(объект) - она определяет конкретно какому классу принадлежит
переданный объект, без учета наследования.
Тогда мы можем расширить встроенные типы, например list:
Можно стать немножко Java-истами!😄 Складывать строки и числа.
Исходя из последних постов - вы уже знаете, как это сделать.
❓ Как вам такие возможности наследования??? 👇
🔥 — Огонь, приму на вооружение!
🤔 — Знал, но освежил память.
💬 — Есть вопросы в комментарии!
Продолжаем сложные, но интересные объяснения! 😈
class Geom: # <=> class Geom(object):
pass
📌 Пользовательский класс по умолчанию наследуется от базового класса object.
Зачем? 🤔 Чтобы обеспечить базовый функционал работы с классами!
g = Geom()
print(g) #👇
# <__main__.Geom object at 0x0000020B7CDF3FD0>
💡Например: вот эта "абракадабра" с адресом объекта при выводе - это результат работы методa
__str__ из класса objectclass Line(Geom):
pass
l = Line()
print(l.__class__)
📌 Наследование может быть ступенчатым, как видим из этого примера. Атрибут
__class__ тоже взят из того же класса objectТри способа проверки на принадлежность класса/объекта к классу:
🔧 С помощью функции issubclass(дочерний_класс, базовый_класс) можем определять,
является ли один класс подклассом другого (именно класс, а не его объекты).
🔧 А вот isinstance(объект, класс) тоже учитывает наследование! 🎯
Эта функция проверяет, является ли объект экземпляром класса или экземпляром любого из его подклассов!
🔧 Есть еще похожая функция type(объект) - она определяет конкретно какому классу принадлежит
переданный объект, без учета наследования.
print(issubclass(Line, Geom)) # True ← Line наследуется от Geom
print(isinstance(l, Geom)) # True ← объект l принадлежит Geom или его подклассу
print(type(l) == Geom) # False ← объект l не является непосредственным экземпляром Geom
💥 Интересный факт: Все стандартные/встроенные типы данных являются классами. (int, float, list, dict, tuple, set, и т.д.)
Тогда мы можем расширить встроенные типы, например list:
class Vector(list):
def __str__(self): # self - здесь просто список
return ' '.join(map(str, self))
v = Vector([1,2, 3])
print(v) # 1 2 3 вместо [1, 2, 3]
Можно стать немножко Java-истами!😄 Складывать строки и числа.
Исходя из последних постов - вы уже знаете, как это сделать.
❓ Как вам такие возможности наследования??? 👇
🔥 — Огонь, приму на вооружение!
🤔 — Знал, но освежил память.
💬 — Есть вопросы в комментарии!
🔥4
Наследование. Атрибуты private и protected
Напомню про режимы доступа свойств класса:
• public - публичное свойство: - доступно везде
• protected - (с одним подчеркиванием
• private - (с двумя подчеркиваниями
Пример: сделаем в базовом классе все атрибуты приватными.
Получим вот такой вывод (хммм): создавали один класс, а там почему-то переменные из другого класса:
Мало того, если даже добавим метод для получения этих координат в классе Rect, то всё равно ничего не получится — будет ошибка:
Однако если мы переместим этот метод в БАЗОВЫЙ класс, то всё отработает как хотелось бы.
Такое поведение потому, что privat-атрибуты доступны только внутри того класса, где объявлены. Собственно для этого они и создавались.
А если хотим получать доступ к ним в дочерних классах, то уменьшаем количество подчёркиваний на одну. Всё просто)
Protected-атрибуты фактически не запрещает обращаться вне класса. Это условности, которые напоминает, чтобы не обращались к этому атрибуту в другом месте. Не для тебя его роза цвела.😅
ываыаа
Тоже самое касается и приватных методов:
Их можно использовать только внутри текущего класса. Если попытаетесь в другом месте — ошибка отсутствия метода.
Можно еще попробовать другой вариант обхода - в дочернем классе вызывать через super() метод базового класса:
Но нет.. .Так тоже не получиться схитрить и ловим ошибку: ``AttributeError: 'super' object has no attribute '_Rect__verify_coord'``
💬 Вопрос к вам:
А вам вообще интересно, полезно про это читать? Может я зря стараюсь и напрягаюсь?
Практически не вижу реакций под такими постами 😔😔😔
Дайте знать в комментах — нужен фидбек!
👇 Как вам такой формат?!
🔥 — Полезно, продолжай!
🤔 — Есть вопросы /предложения или темы для следующих постов.
Напомню про режимы доступа свойств класса:
• public - публичное свойство: - доступно везде
• protected - (с одним подчеркиванием
_attr): для обращения внутри класса и во всех его дочерних классах• private - (с двумя подчеркиваниями
__attr): для обращения только внутри классаПример: сделаем в базовом классе все атрибуты приватными.
class Geom:
name = 'Geom'
def __init__(self, x1, y1, x2, y2):
print(f"инициализатор Geom для {self.__class__}")
self.__x1 = x1
self.__y1 = y1
self.__x2 = x2
self.__y2 = y2
class Rect(Geom):
def __init__(self, x1, y1, x2, y2, fill='red'):
super().__init__(x1, y1, x2, y2)
self.__fill = fill
r = Rect(1, 2, 3 ,4)
print(r.__dict__)
Получим вот такой вывод (хммм): создавали один класс, а там почему-то переменные из другого класса:
{'_Geom__x1': 1, '_Geom__y1': 2, '_Geom__x2': 3, '_Geom__y2': 4, '_Rect__fill': 'red'}Мало того, если даже добавим метод для получения этих координат в классе Rect, то всё равно ничего не получится — будет ошибка:
def get_coords(self):
return (self.__x1, self.__y1)
print(r.get_coords()) # # AttributeError: 'Rect' object has no attribute '_Rect__x1'
Однако если мы переместим этот метод в БАЗОВЫЙ класс, то всё отработает как хотелось бы.
Такое поведение потому, что privat-атрибуты доступны только внутри того класса, где объявлены. Собственно для этого они и создавались.
А если хотим получать доступ к ним в дочерних классах, то уменьшаем количество подчёркиваний на одну. Всё просто)
Protected-атрибуты фактически не запрещает обращаться вне класса. Это условности, которые напоминает, чтобы не обращались к этому атрибуту в другом месте. Не для тебя его роза цвела.😅
ываыаа
Тоже самое касается и приватных методов:
def __verify_coord(self, coord):
return 0 <= coord <= 100
Их можно использовать только внутри текущего класса. Если попытаетесь в другом месте — ошибка отсутствия метода.
Можно еще попробовать другой вариант обхода - в дочернем классе вызывать через super() метод базового класса:
super().__verify_coord(x1)
Но нет.. .Так тоже не получиться схитрить и ловим ошибку: ``AttributeError: 'super' object has no attribute '_Rect__verify_coord'``
💬 Вопрос к вам:
А вам вообще интересно, полезно про это читать? Может я зря стараюсь и напрягаюсь?
Практически не вижу реакций под такими постами 😔😔😔
Дайте знать в комментах — нужен фидбек!
👇 Как вам такой формат?!
🔥 — Полезно, продолжай!
🤔 — Есть вопросы /предложения или темы для следующих постов.
🔥5
Здесь можно пройти тест на "Какой ты язык программирования". У меня, как видите, совпало) А у вас что вышло?
😈4😁2
Полиморфизм и абстрактные методы
Полиморфизм – это возможность работы с совершенно разными объектами (языка Python) единым образом. (т.е. через единый интерфейс)
Разберемся на примере: пусть есть два класса: для прямоугольников и квадратов:
Получить периметры очевидно можем так:
Но... Если захотим перебрать элементы некого списка объектов [r1, r2, sq1, sq2] и через цикл получить значение периметра, то это будет проблематичнее, ведь у них разные методы для получения данного значения. Можно, конечно, сделать проверку на принадлежность объекта какому-то классу и потом уже вызывать соответствующий метод.
Например так:
А если еще один или несколько классов добавиться, что тогда делать? Добавлять каждый раз ветку и плодить 100500 условий? В данном случае код получается не очень гибким.
Это можно исправить. Вот как раз здесь очень хорошо применим подход, который и называется полиморфизмом. Мы договоримся в каждом классе создавать методы с одинаковыми именами, например - get_pr(). Получается что к разным объектам мы обращаемся через единый интерфейс - это и есть полиморфизм.
Но можно еще немного улучшить эту методику.
Для чего? А в друг мы забудем где-то создать метод для получения периметра. Я, например, могу что-то забыть вполне спокойно😬 Тут есть два пути решения проблемы. Сначала создаем базовый класс а остальным - унаследоваться, затем:
1 способ) В нем прописать метод get_pr().
2 способ) Реализовать через абстрактный метод
• В 1 варианте мы не будем получать ошибок, при вызове - да. Но результат будет не тот, который ожидаем.
• Во 2 варианте будет выдаваться ошибка, которая конкретно указывает что мы забыли переопределить метод в дочернем классе.
Итоговая реализация выглядит вот так:
Подытожим. В языках программирования методы, которые обязательно нужно переопределять в дочерних классах и которые не имеют своей собственной реализации называют абстрактными.
Вы можете заметить, что есть еще абстрактные классы и абстрактные методы. Но об этом в другой раз😏
Комментарии и реакции приветствуются👇
🔥 — Огонь, приму на вооружение!
🤔 — Знал, но освежил память.
💬 — Есть вопросы в комментарии!
Полиморфизм – это возможность работы с совершенно разными объектами (языка Python) единым образом. (т.е. через единый интерфейс)
Разберемся на примере: пусть есть два класса: для прямоугольников и квадратов:
class Rectangle:
def __init__(self, w, h):
self.w = w
self.h = h
def get_rect_pr(self):
return 2 * (self.w + self.h)
class Square:
def __init__(self, a):
self.a = a
def get_sq_pr(self):
return 4 * self.a
r1 = Rectangle(1, 2)
r2 = Rectangle(3, 4)
sq1 = Square(10)
sq2 = Square(20)
Получить периметры очевидно можем так:
print(r1.get_rect_pr(), r2.get_rect_pr())
print(sq1.get_sq_pr(), sq2.get_sq_pr())
Но... Если захотим перебрать элементы некого списка объектов [r1, r2, sq1, sq2] и через цикл получить значение периметра, то это будет проблематичнее, ведь у них разные методы для получения данного значения. Можно, конечно, сделать проверку на принадлежность объекта какому-то классу и потом уже вызывать соответствующий метод.
Например так:
geom = [r1, r2, sq1, sq2]
for g in geom:
if isinstance(g, Rectangle):
print(g.get_rect_pr())
else:
print(g.get_sq_pr())
А если еще один или несколько классов добавиться, что тогда делать? Добавлять каждый раз ветку и плодить 100500 условий? В данном случае код получается не очень гибким.
class Triangle:
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
def get_tr_pr(self):
return self.a + self.b + self.c
Это можно исправить. Вот как раз здесь очень хорошо применим подход, который и называется полиморфизмом. Мы договоримся в каждом классе создавать методы с одинаковыми именами, например - get_pr(). Получается что к разным объектам мы обращаемся через единый интерфейс - это и есть полиморфизм.
Но можно еще немного улучшить эту методику.
Для чего? А в друг мы забудем где-то создать метод для получения периметра. Я, например, могу что-то забыть вполне спокойно😬 Тут есть два пути решения проблемы. Сначала создаем базовый класс а остальным - унаследоваться, затем:
1 способ) В нем прописать метод get_pr().
def get_pr(self):
return -1
2 способ) Реализовать через абстрактный метод
def get_pr(self):
raise NotImplementedError(f"В дочернем классе: {self.__class__.__name__} должен быть переопределен метод get_pr()")
)
• В 1 варианте мы не будем получать ошибок, при вызове - да. Но результат будет не тот, который ожидаем.
• Во 2 варианте будет выдаваться ошибка, которая конкретно указывает что мы забыли переопределить метод в дочернем классе.
Итоговая реализация выглядит вот так:
class Geom:
def get_pr(self):
raise NotImplementedError(f"В дочернем классе: {self.__class__.__name__} должен быть переопределен метод get_pr()")
class Rectangle(Geom):
...
def get_pr(self):
return 2 * (self.w + self.h)
...
class Square(Geom):
...
class Triangle(Geom):
...
Подытожим. В языках программирования методы, которые обязательно нужно переопределять в дочерних классах и которые не имеют своей собственной реализации называют абстрактными.
Вы можете заметить, что есть еще абстрактные классы и абстрактные методы. Но об этом в другой раз😏
Комментарии и реакции приветствуются👇
🔥 — Огонь, приму на вооружение!
🤔 — Знал, но освежил память.
💬 — Есть вопросы в комментарии!
🔥4
🤔 ПРО МОДУЛЬ abc 🤔
В языке Python есть еще один распространенный способ объявления абстрактных методов класса через декоратор abstractmethod модуля abc:
Чтобы корректно работал декоратор abstractmethod сам класс должен наследоваться от базового класса ABC. Например, так:
Мы здесь имеем два абстрактных метода внутри класса Transport, причем, первый метод go() - это обычный метод, а второй abstract_class_method() - это абстрактный метод уровня класса. Обратите внимание на порядок использования декораторов classmethod и abstractmethod. Они должны быть записаны именно в такой последовательности.
Теперь, если объявить какой-либо дочерний класс, например:
То в нем обязательно нужно переопределить абстрактные методы go и abstract_class_method класса Transport. Иначе, объект класса Bus не будет создан (возникнет исключение TypeError).
Также с помощью модуля abc можно определять не только абстрактные методы, но и абстрактные объекты-свойства (property). Делается это следующим образом:
Порядок навешивания декораторов важен! Конечно, вы можете в базовом классе Transport не прописывать "property", и без этого сработает. Но так лучше все-таки делать, чтобы понимать как именно надо реализовывать этот метод в дочернем классе.
Комментарии и реакции приветствуются👇
🔥 — Огонь, приму на вооружение!
🤔 — Знал, но освежил память.
💬 — Есть вопросы в комментарии!
В языке Python есть еще один распространенный способ объявления абстрактных методов класса через декоратор abstractmethod модуля abc:
from abc import ABC, abstractmethod
Чтобы корректно работал декоратор abstractmethod сам класс должен наследоваться от базового класса ABC. Например, так:
class Transport(ABC):
@abstractmethod
def go(self):
"""Метод для перемещения транспортного средства"""
@classmethod
@abstractmethod
def abstract_class_method(cls):
"""Абстрактный метод класса"""
Мы здесь имеем два абстрактных метода внутри класса Transport, причем, первый метод go() - это обычный метод, а второй abstract_class_method() - это абстрактный метод уровня класса. Обратите внимание на порядок использования декораторов classmethod и abstractmethod. Они должны быть записаны именно в такой последовательности.
Теперь, если объявить какой-либо дочерний класс, например:
class Bus(Transport):
def __init__(self, model, speed):
self._model = model
self._speed = speed
def go(self):
print("bus go")
@classmethod
def abstract_class_method(cls):
pass
То в нем обязательно нужно переопределить абстрактные методы go и abstract_class_method класса Transport. Иначе, объект класса Bus не будет создан (возникнет исключение TypeError).
Также с помощью модуля abc можно определять не только абстрактные методы, но и абстрактные объекты-свойства (property). Делается это следующим образом:
from abc import ABC, abstractmethod
class Transport(ABC):
@abstractmethod
def go(self):
"""Метод для перемещения транспортного средства"""
@property
@abstractmethod
def speed(self):
"""Абстрактный объект-свойство getter 'speed' """
@speed.setter
@abstractmethod
def speed(self, value):
"""Абстрактный объект-свойство setter 'speed' """
Порядок навешивания декораторов важен! Конечно, вы можете в базовом классе Transport не прописывать "property", и без этого сработает. Но так лучше все-таки делать, чтобы понимать как именно надо реализовывать этот метод в дочернем классе.
Комментарии и реакции приветствуются👇
🔥 — Огонь, приму на вооружение!
🤔 — Знал, но освежил память.
💬 — Есть вопросы в комментарии!
🔥5
👿Алгоритм Дейкстры или куда пропал мой вчерашний день.👿
Всём доброе утро. Курс по ООП потихоньку подходит к концу. Подошло время очередного испытания - большая задача по темам главы. Как вы возможно догадались, здесь про граф, и к сожалению не про Дракула.
Алгоритм Дейкстры - нахождение кратчайшего пути от вершины до всех остальных вершине в графе, где каждое ребро графа имеет неотрицательную стоимость... .и т.д - загуглите, кому станет интересно.
Собственно сел решать, на 95% всё описал по ТЗ. Казалось бы почти все. Но как же я ошибался😅Это оказалось лишь верхушкой айсберга)
И тут началось самое интересное. Сначала пришлось гуглить и вспоминать как там вообще всё происходит. Последний раз проходил это в универе. Разобраться на примере на бумажке, . Ну вроде бы всё легко и понятно. Осталось дело за малым перенести алгоритм на комп.
Пару подходов с перерывами и я всё ещё практически не сдвинулся с места. Крч, недоделал до конца и забил. Конечно можно было взять готовой алгоритм. Но для чего?) Даже в статейке, где я прочитал про Дейкстру, был пример алгоритма на питоне. Чуть не проспойлерил себе решение. Предлагают импортировать доп. модуль, который предназначен для работы с кучами. После этих слов я перестал читать дальше)) Кажется можно реализовать проще, без всяких этих изыщрений.
А у вас как дела обстоят с графами?)
Всём доброе утро. Курс по ООП потихоньку подходит к концу. Подошло время очередного испытания - большая задача по темам главы. Как вы возможно догадались, здесь про граф, и к сожалению не про Дракула.
Алгоритм Дейкстры - нахождение кратчайшего пути от вершины до всех остальных вершине в графе, где каждое ребро графа имеет неотрицательную стоимость... .и т.д - загуглите, кому станет интересно.
Собственно сел решать, на 95% всё описал по ТЗ. Казалось бы почти все. Но как же я ошибался😅Это оказалось лишь верхушкой айсберга)
И тут началось самое интересное. Сначала пришлось гуглить и вспоминать как там вообще всё происходит. Последний раз проходил это в универе. Разобраться на примере на бумажке, . Ну вроде бы всё легко и понятно. Осталось дело за малым перенести алгоритм на комп.
Пару подходов с перерывами и я всё ещё практически не сдвинулся с места. Крч, недоделал до конца и забил. Конечно можно было взять готовой алгоритм. Но для чего?) Даже в статейке, где я прочитал про Дейкстру, был пример алгоритма на питоне. Чуть не проспойлерил себе решение. Предлагают импортировать доп. модуль, который предназначен для работы с кучами. После этих слов я перестал читать дальше)) Кажется можно реализовать проще, без всяких этих изыщрений.
А у вас как дела обстоят с графами?)
🎃4🔥2
👁Множественное наследование в Python👁
Это когда класс наследуется от нескольких родителей. Не самый частый, но мощный инструмент!
📚 Часть 1: Простой пример
🎯 Главный кейс — миксины (примеси) — классы, которые добавляют одну конкретную фичу другим классам.
👉 Простой пример:
Что происходит?
1. Создаем Notebook → вызывается Goods.init
2 . super().init() в Goods передает управление MixinLog.init
3. Оба инициализатора выполняются
🧠 Как работает super()?
Раньше, уже думаю, ее видели и понимаете что она делает. Я тоже так думал, но оказалось не совсем верно понимал🫣
Оказывается super() возвращает не просто "родительский класс", а следующий класс в MRO (Method Resolution Order) — порядке обхода классов при наследовании.
👉 Цепочка вызовов в данном примере:
1. Notebook() → Goods.init()
2. super().init() в Goods вызывает MixinLog.init()
3. super().init() в MixinLog вызывает object.init()
4. Все инициализаторы выполнены!
🔍 Важно: порядок имеет значение!
Порядок наследования (Goods, MixinLog) определяет MRO (Method Resolution Order):
💎 Правила для миксинов:
1. Делайте их максимально простыми и узкоспециализированными
2. Всегда вызывайте super().init() в init
3. Ставьте миксины после основного класса в наследовании
🎯 Зачем это нужно?
• Переиспользование кода без дублирования
• Гибкое расширение функциональности
• Чистая архитектура (разделение ответственности)
📌 Итог:
Миксины — это как LEGO для классов. Добавляйте готовые блоки функциональности, не ломая основную логику!
А вы использовали множественное наследование или миксины на практике? 👇
Это когда класс наследуется от нескольких родителей. Не самый частый, но мощный инструмент!
📚 Часть 1: Простой пример
🎯 Главный кейс — миксины (примеси) — классы, которые добавляют одну конкретную фичу другим классам.
👉 Простой пример:
class Goods: # Товары
def __init__(self, name, weight, price):
super().__init__() # 🔑 ключевой момент!
self.name = name
self.weight = weight
self.price = price
def print_info(self):
print(f"{self.name}, {self.weight}, {self.price}")
class MixinLog:
ID = 0
def __init__(self):
super().__init__() # здесь для примера
MixinLog.ID += 1
self.id = MixinLog.ID
def save_sell_log(self):
print(f"Товар с id={self.id} был продан в {datetime.now()}")
# Множественное наследование
class Notebook(Goods, MixinLog):
pass
n = Notebook('Acer', 1.5, 30000)
n.print_info() # Acer, 1.5, 30000
n.save_sell_log() # ✅ работает!
Что происходит?
1. Создаем Notebook → вызывается Goods.init
2 . super().init() в Goods передает управление MixinLog.init
3. Оба инициализатора выполняются
🧠 Как работает super()?
Раньше, уже думаю, ее видели и понимаете что она делает. Я тоже так думал, но оказалось не совсем верно понимал🫣
Оказывается super() возвращает не просто "родительский класс", а следующий класс в MRO (Method Resolution Order) — порядке обхода классов при наследовании.
👉 Цепочка вызовов в данном примере:
1. Notebook() → Goods.init()
2. super().init() в Goods вызывает MixinLog.init()
3. super().init() в MixinLog вызывает object.init()
4. Все инициализаторы выполнены!
🔍 Важно: порядок имеет значение!
Порядок наследования (Goods, MixinLog) определяет MRO (Method Resolution Order):
print(Notebook.__mro__)
# (<class '__main__.Notebook'>,
# <class '__main__.Goods'>,
# <class '__main__.MixinLog'>,
# <class 'object'>)
💎 Правила для миксинов:
1. Делайте их максимально простыми и узкоспециализированными
2. Всегда вызывайте super().init() в init
3. Ставьте миксины после основного класса в наследовании
🎯 Зачем это нужно?
• Переиспользование кода без дублирования
• Гибкое расширение функциональности
• Чистая архитектура (разделение ответственности)
📌 Итог:
Миксины — это как LEGO для классов. Добавляйте готовые блоки функциональности, не ломая основную логику!
А вы использовали множественное наследование или миксины на практике? 👇
👍4
📚 Часть 2: MRO и конфликты методов в Python
🔍 Что такое MRO на самом деле?
MRO (Method Resolution Order) — это алгоритм, который определяет порядок поиска методов и атрибутов при множественном наследовании.
🎯 Как Python вычисляет MRO?
Используется алгоритм C3 Linearization. Главные правила:
• Дочерний класс всегда перед родительским
• Порядок наследования сохраняется
• Более левые классы в наследовании имеют приоритет
⚔️ Разрешение конфликтов методов
Рассмотрим ситуацию, когда несколько родительских классов имеют методы с одинаковыми именами, и как можно управлять их вызовом:
🔍 1-й вариант: Оставить как есть (используется первый по MRO)
Почему? MRO для класса MFP: MFP → Printer → Scanner → Copywriter → object. Python находит метод action() в классе Printer первым и останавливается.
🎯 2-й вариант: явный вызов метода из конкретного класса
Важно: При таком вызове нужно явно передавать объект (mfp) первым аргументом, так как мы вызываем метод класса, а не объекта.
🔧 3-й вариант: Переопределить метод в дочернем классе
📊 Когда что использовать:
1-й способ — когда порядок MRO вас устраивает и конфликтов нет
2-й способ — для разового вызова метода из конкретного родителя
3-й способ — для постоянного изменения поведения или комбинирования логики
⚠️ Замечание:
Хотя явный вызов методов через Класс.метод(объект) работает, это жёсткая привязка к конкретному классу. Если изменится иерархия наследования, такой код может сломаться. Переопределение метода в дочернем классе — более гибкий и поддерживаемый подход.
💡Напоминаю - проверить MRO можно всегда:
Вывод: В Python есть гибкие механизмы для работы с наследованием. Выбирайте подход в зависимости от задачи — от простого использования MRO до полного контроля через переопределение методов.
🔍 Что такое MRO на самом деле?
MRO (Method Resolution Order) — это алгоритм, который определяет порядок поиска методов и атрибутов при множественном наследовании.
class A:
def method(self):
print("A")
class B(A):
def method(self):
print("B")
class C(A):
def method(self):
print("C")
class D(B, C):
pass
print(D.__mro__)
# (<class '__main__.D'>, <class '__main__.B'>,
# <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
d = D()
d.method() # Вывод: "B" (первый в MRO)
🎯 Как Python вычисляет MRO?
Используется алгоритм C3 Linearization. Главные правила:
• Дочерний класс всегда перед родительским
• Порядок наследования сохраняется
• Более левые классы в наследовании имеют приоритет
⚔️ Разрешение конфликтов методов
Рассмотрим ситуацию, когда несколько родительских классов имеют методы с одинаковыми именами, и как можно управлять их вызовом:
class Printer:
def action(self):
return "Печатаю"
class Scanner:
def action(self):
return "Сканирую"
class Copywriter:
def action(self):
return "Копирую"
class MFP(Printer, Scanner, Copywriter):
pass
🔍 1-й вариант: Оставить как есть (используется первый по MRO)
mfp = MFP()
print(mfp.action()) # 👉 "Печатаю"
Почему? MRO для класса MFP: MFP → Printer → Scanner → Copywriter → object. Python находит метод action() в классе Printer первым и останавливается.
🎯 2-й вариант: явный вызов метода из конкретного класса
print(Scanner.action(mfp)) # 👉 "Сканирую"
Важно: При таком вызове нужно явно передавать объект (mfp) первым аргументом, так как мы вызываем метод класса, а не объекта.
🔧 3-й вариант: Переопределить метод в дочернем классе
class MFP(Printer, Scanner, Copywriter):
def action(self):
return Scanner.action(self)
# Своя уникальная логика
# return "Я МФУ, делаю всё!"
mfp2 = MFP()
print(mfp2.action()) # "Копирую"
📊 Когда что использовать:
1-й способ — когда порядок MRO вас устраивает и конфликтов нет
2-й способ — для разового вызова метода из конкретного родителя
3-й способ — для постоянного изменения поведения или комбинирования логики
⚠️ Замечание:
Хотя явный вызов методов через Класс.метод(объект) работает, это жёсткая привязка к конкретному классу. Если изменится иерархия наследования, такой код может сломаться. Переопределение метода в дочернем классе — более гибкий и поддерживаемый подход.
💡Напоминаю - проверить MRO можно всегда:
print(MFP.__mro__)
# (<class '__main__.MFP'>, <class '__main__.Printer'>,
# <class '__main__.Scanner'>, <class '__main__.Copywriter'>, <class 'object'>)
Вывод: В Python есть гибкие механизмы для работы с наследованием. Выбирайте подход в зависимости от задачи — от простого использования MRO до полного контроля через переопределение методов.
🔥3