Сравнения в Python: как научить классы понимать ==, !=, >, <, >=, <= 🔍
Продолжаем разбирать магические методы! После арифметики из прошлого поста переходим к сравнениям:
eq() – для равенства ==
ne() – для неравенства !=
lt() – для оператора меньше <
le() – для оператора меньше или равно <=
gt() – для оператора больше >
ge() – для оператора больше или равно >=
По умолчанию первый и второй метод сравнивают 🆔 объектов. Поэтому технически норм, при вызове ошибок не будет. Но реализация конечно будет не такой как вы ожидали увидеть). А если для того же класса Clock из прошлого поста сравнивать другими операциями, то вообще вылезут ошибки.
Но их можно легко исправить.
Пример:
Другие методы можете сами догадаться как прописывать, аналогично этому
Занимательный факт: можно дополнительно не реализовывать метод для сравнения на неравенство - (ne) , если уже реализован (eq). Как думаете почему? А если реализовали метод "<", сработает ли ">"?🧐🧐🧐
Подсказка: Python пытается быть умным и использовать имеющиеся методы для обратных операций! 🔄
Продолжаем разбирать магические методы! После арифметики из прошлого поста переходим к сравнениям:
eq() – для равенства ==
ne() – для неравенства !=
lt() – для оператора меньше <
le() – для оператора меньше или равно <=
gt() – для оператора больше >
ge() – для оператора больше или равно >=
По умолчанию первый и второй метод сравнивают 🆔 объектов. Поэтому технически норм, при вызове ошибок не будет. Но реализация конечно будет не такой как вы ожидали увидеть). А если для того же класса Clock из прошлого поста сравнивать другими операциями, то вообще вылезут ошибки.
Но их можно легко исправить.
def __eq__(self, other):
if not isinstance(other, (int, Clock)):
raise TypeError('Оперант справа должен иметь тип int или Clock')
other_seconds = other if isinstance(other, int) else other.seconds
return self.seconds == other_seconds
Пример:
c1 = Clock(1000)
c2 = Clock(1000)
print(c1 == c2) # True
Другие методы можете сами догадаться как прописывать, аналогично этому
__eq__Занимательный факт: можно дополнительно не реализовывать метод для сравнения на неравенство - (ne) , если уже реализован (eq). Как думаете почему? А если реализовали метод "<", сработает ли ">"?🧐🧐🧐
Подсказка
🔥3
"Вайбкодер" vs Программист: Новая эра или просто хайп?
Смотрю сейчас вакансии и натыкаюсь вот на такую. Название "Вайбкодер" - интересно))
И сразу мысль: может я не тем занимаюсь и зря всё это учу? Чисто взял, научился создавать промпты для нейронки — и ты уже в IT? Кажется, что это легкий вход, особенно когда видишь "опыт не требуется" и "удаленка".
Но за этим хайповым названием скрываются вполне реальные технические требования: знание Python, работа с API, автоматизация, n8n, VS Code, OpenAI и Claude. Это не просто "поболтать с чат-ботом".
Всем, кому интересно полное описание - вот ссылка на вакансию.
Мое мнение - нет, это не замена классическому кодингу(хотя я задумался😅). Просто новая ниша. Учиться все равно нужно много. А вы как считаете?
Смотрю сейчас вакансии и натыкаюсь вот на такую. Название "Вайбкодер" - интересно))
И сразу мысль: может я не тем занимаюсь и зря всё это учу? Чисто взял, научился создавать промпты для нейронки — и ты уже в IT? Кажется, что это легкий вход, особенно когда видишь "опыт не требуется" и "удаленка".
Но за этим хайповым названием скрываются вполне реальные технические требования: знание Python, работа с API, автоматизация, n8n, VS Code, OpenAI и Claude. Это не просто "поболтать с чат-ботом".
Всем, кому интересно полное описание - вот ссылка на вакансию.
Мое мнение - нет, это не замена классическому кодингу(хотя я задумался😅). Просто новая ниша. Учиться все равно нужно много. А вы как считаете?
🤔5
Вычисление хеша для объектов класса в Python
Функция hash() в Python вычисляет хеш для неизменяемых объектов (immutable types):
Если вы попытаетесь взять хеш от изменяемых объектов (например, списка list), то получите ошибку TypeError: unhashable type: 'list'. 😱
Ошибка возникнет даже в том случае, если изменяемый элемент находится внутри неизменяемого объекта. Пример: элементом кортежа является список list. Тогда тоже будет ошибка "unhashable".
Некоторые свойства хешей:
1) Для одинаковых объектов хеши всегда одинаковы.
2) Обратное утверждение в общем случае не верно (разные объекты могут иметь одинаковый хеш — это называется коллизией).
3) Если хеши не равны, то и сами объекты точно не равны.
Зачем это вообще нужно? 🤔
Мы поняли, что такое хеш, теперь давайте разберемся, зачем он нам пригодился! Например, словари используют хеши для быстрого доступа к ключам для быстрого поиска. Ведь помните, словари в ключах требуют только неизменяемые типы данных? Словарь хранит ключи примерно в таком виде: (хеш_ключа, значение_ключа).
А это зачем нужно? Первоначально запись в словаре ищется по хешу, что занимает минимум времени. Далее, если вдруг хеши разных ключей совпали (коллизия), то тогда уже Python дополнительно проверяет объекты на равенство (==).
Хеширование на примере классов
Теперь рассмотрим, как это работает с пользовательскими классами:
Как думаете? какие будут хеши у этих экземпляров класса? Одинаковые, разные, или вообще ошибка?Разные!
Если p1 == p2 возвращает True, то хеши одинаковые. А если False, то разные(если нет коллизии). Почему так? Коллизия? Нет, все проще. По умолчанию Python вычисляет хеш на основе id объекта (его адреса в памяти) 🧠
Переопределение `__eq__` и `__hash__`
А что если мы переопределим метод
Тогда p1 == p2 станет True. Значит и хеши по идее равны. Но не все так просто) Вылезет ошибка. Хе-хе🙃. Когда мы переопределилиИными словами: после переопределения __eq__: Python автоматически делает класс unhashable (`__hash__ = None`), если мы не определили свой `__hash__`. Это защита от нарушения контракта между равенством и хешированием.
Чтобы это исправить, нужно обязательно переопределить метод
Что теперь скажете насчет хешей объектов p1 и p2?Равны! 🎉 Мы теперь вычисляем хеш не от самих объектов (их ID в памяти), а от их координат.
Пример использования в словаре
Например, если по такой логике создавать словарь👇, то получим только одну пару ключ: значение. А если мы не переопределяли эти методы, то получили бы две.
Таким образом, мы можем переопределять методы eq и hash под требуемую логику нашей программы. В данном случае, объекты класса Point с одинаковыми координатами воспринимаются как одинаковые объекты. 😏
А вы когда-нибудь сталкивались с ошибкой "unhashable type" в своих проектах? 😁
Функция hash() в Python вычисляет хеш для неизменяемых объектов (immutable types):
a = hash(123) # 123
b = hash("Python") # -8344082464644304368
c = hash((1, 2, 3)) # 529344067295497451
Если вы попытаетесь взять хеш от изменяемых объектов (например, списка list), то получите ошибку TypeError: unhashable type: 'list'. 😱
Ошибка возникнет даже в том случае, если изменяемый элемент находится внутри неизменяемого объекта. Пример: элементом кортежа является список list. Тогда тоже будет ошибка "unhashable".
Некоторые свойства хешей:
1) Для одинаковых объектов хеши всегда одинаковы.
2) Обратное утверждение в общем случае не верно (разные объекты могут иметь одинаковый хеш — это называется коллизией).
3) Если хеши не равны, то и сами объекты точно не равны.
Зачем это вообще нужно? 🤔
Мы поняли, что такое хеш, теперь давайте разберемся, зачем он нам пригодился! Например, словари используют хеши для быстрого доступа к ключам для быстрого поиска. Ведь помните, словари в ключах требуют только неизменяемые типы данных? Словарь хранит ключи примерно в таком виде: (хеш_ключа, значение_ключа).
А это зачем нужно? Первоначально запись в словаре ищется по хешу, что занимает минимум времени. Далее, если вдруг хеши разных ключей совпали (коллизия), то тогда уже Python дополнительно проверяет объекты на равенство (==).
Хеширование на примере классов
Теперь рассмотрим, как это работает с пользовательскими классами:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
p1 = Point(1, 2)
p2 = Point(1, 2)
print(hash(p1))
print(hash(p2))
Как думаете? какие будут хеши у этих экземпляров класса? Одинаковые, разные, или вообще ошибка?
Если p1 == p2 возвращает True, то хеши одинаковые. А если False, то разные(если нет коллизии). Почему так? Коллизия? Нет, все проще. По умолчанию Python вычисляет хеш на основе id объекта (его адреса в памяти) 🧠
Переопределение `__eq__` и `__hash__`
А что если мы переопределим метод
__eq__ в классе Point следующим образом?def __eq__(self, other):
return self.x == other.x and self.y == other.y
Тогда p1 == p2 станет True. Значит и хеши по идее равны. Но не все так просто) Вылезет ошибка. Хе-хе🙃. Когда мы переопределили
__eq__, то функция hash() перестает работать. Т.к. перестает работать стандартный алгоритм вычисления хеша. Чтобы это исправить, нужно обязательно переопределить метод
__hash__ в нашем классе. Он и вызывается (логично, да?), когда мы пишем hash(p1).def __hash__(self):
return hash((self.x, self.y))
Что теперь скажете насчет хешей объектов p1 и p2?
Пример использования в словаре
Например, если по такой логике создавать словарь👇, то получим только одну пару ключ: значение. А если мы не переопределяли эти методы, то получили бы две.
d = {}
d[p1] = 1
d[p2] = 2
print(d) # {<__main__.Point object at 0x0000024D012C8F40>: 2}Таким образом, мы можем переопределять методы eq и hash под требуемую логику нашей программы. В данном случае, объекты класса Point с одинаковыми координатами воспринимаются как одинаковые объекты. 😏
А вы когда-нибудь сталкивались с ошибкой "unhashable type" в своих проектах? 😁
🔥3
Сегодня поговорим о способах настройки "правдивости" объектов класса 🤔
Когда к экземпляру класса применяется функция 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