Ответ
Магические методы, также известные как "dunder" методы (от "double underscore" — двойное подчеркивание), — это специальные методы в Python, имена которых начинаются и заканчиваются двойным подчеркиванием (например, __init__, __str__). Они позволяют классам реализовывать определенные протоколы и переопределять поведение встроенных операций и функций.
Назначение: Dunder методы используются для:
- Операторной перегрузки: Изменение поведения стандартных операторов (например,
+,-,==,[]) для пользовательских объектов. - Реализации протоколов: Позволяют объектам вести себя как контейнеры, итераторы, числовые типы и т.д., интегрируясь с функциями Python (например,
len(),str(),for...in). - Управления жизненным циклом объекта: Инициализация, удаление, создание.
Основные категории и примеры:
-
Инициализация и удаление объектов:
__new__(cls, ...): Вызывается для создания нового экземпляра класса.__init__(self, ...): Конструктор, инициализирует созданный объект.__del__(self): Деструктор, вызывается при удалении объекта (не гарантировано).
-
Строковое представление:
__str__(self): Определяет "официальное" строковое представление объекта, используемое функциейstr()иprint(). Должно быть читабельным для пользователя.__repr__(self): Определяет "неофициальное" строковое представление, используемое функциейrepr()и в интерактивной консоли. Должно быть однозначным и, по возможности, позволять воссоздать объект.
-
Сравнение объектов (операторы сравнения):
__eq__(self, other):self == other__ne__(self, other):self != other__lt__(self, other):self < other__le__(self, other):self <= other__gt__(self, other):self > other__ge__(self, other):self >= other
-
Контейнерные протоколы (доступ к элементам):
__len__(self):len(self)__getitem__(self, key):self[key](доступ по индексу/ключу)__setitem__(self, key, value):self[key] = value__delitem__(self, key):del self[key]__contains__(self, item):item in self
-
Арифметические операции:
__add__(self, other):self + other__sub__(self, other):self - other__mul__(self, other):self * other- И многие другие (
__truediv__,__floordiv__,__mod__,__pow__и т.д.).
Пример использования:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
"""Представление для разработчика."""
return f"Point(x={self.x}, y={self.y})"
def __str__(self):
"""Представление для пользователя."""
return f"({self.x}, {self.y})"
def __add__(self, other):
"""Перегрузка оператора сложения для сложения двух точек."""
if isinstance(other, Point):
return Point(self.x + other.x, self.y + other.y)
raise TypeError("Можно складывать только с объектом Point")
def __eq__(self, other):
"""Перегрузка оператора равенства."""
if isinstance(other, Point):
return self.x == other.x and self.y == other.y
return NotImplemented # Позволяет другому объекту попробовать сравнение
p1 = Point(1, 2)
p2 = Point(3, 4)
p3 = Point(1, 2)
print(p1) # Вызывает __str__: (1, 2)
print(repr(p1)) # Вызывает __repr__: Point(x=1, y=2)
p_sum = p1 + p2
print(f"Сумма точек: {p_sum}") # (4, 6)
print(f"p1 == p2: {p1 == p2}") # False
print(f"p1 == p3: {p1 == p3}") # True Ответ 18+ 🔞
А, вот эта тема про магические методы, ёпта! Это же, блядь, основа основ, на которой всё держится, как тот самый Герасим на крыше. Ты думаешь, что твой объект — это просто куча атрибутов? Хуй там! Это целая вселенная, блядь, со своими законами, и эти законы — ду́ндеры.
Суть, на пальцах:
Это такие методы, которые начинаются и заканчиваются двойным подчёркиванием. __init__, __str__, __add__ — вот это всё. Их ещё «дундер» называют, потому что «дабл андерскор». Ну, а магическими — потому что они, сука, реально творят магию. Они говорят Питону: «Слушай, падла, когда ты будешь пытаться сложить два моих объекта через +, не лезь со своим стандартным поведением, а сделай вот так, как я тебе сказал!»
Зачем это всё, нахуй? А чтобы твои объекты вели себя не как безмозглые куски памяти, а как настоящие граждане языка! Чтобы они умели:
- Перегружать операторы: Сделать так, чтобы
объект1 + объект2делал не хуйню, а именно то, что тебе нужно. Складывал вектора, деньги, или, там, пиццы с ананасами, я не знаю. - Встраиваться в систему: Чтобы твой объект работал с
len(),str(),for,in— со всеми этими стандартными штуками. Чтобы он вёл себя как контейнер, как число, как итератор — да как угодно! - Контролировать свою судьбу: Родиться красиво (
__init__), предстать перед людьми в лучшем свете (__str__), и, когда придёт время, уйти с достоинством (ну,__del__, хотя на него надейся, но проверяй).
Основные банды дундеров:
-
Родители и могильщики:
__new__(cls, ...): Это тот, кто создаёт объект из ничего. Редко трогаешь.__init__(self, ...): А это — конструктор, наш главный по тарелочкам. Он берёт голый созданный объект и начиняет его данными. «Родился, блядь, вот тебеxиy, живи».__del__(self): Деструктор. Вызывается, когда объект собирают на свалку истории. Ненадёжный тип, на него особо не рассчитывай.
-
Пиар-отдел (строковое представление):
__str__(self): Для красоты. Что выведетprint(объект)илиstr(объект). Должно быть человекочитаемо.__repr__(self): Для отладки и разработчиков. Что покажет просто имя объекта в консоли илиrepr(объект). Должно быть однозначно и, в идеале, позволять скопипастить и создать такой же объект. Разницу чувствуешь? Один — для людей, другой — для таких же, как мы, ебланов, которые код дебажат.
-
Судьи и сравниватели:
__eq__,__ne__,__lt__,__le__,__gt__,__ge__: Это всё про сравнение.==,!=,<,<=,>,>=. Без них твои объекты даже сравнить нормально нельзя, пидарас шерстяной.
-
Контейнерная братва:
__len__(self): Отвечает на вопросlen(объект).__getitem__(self, key): Позволяет делатьобъект[ключ]— доставать элемент.__setitem__(self, key, value): Позволяет делатьобъект[ключ] = значение— класть элемент.__delitem__(self, key):del объект[ключ]— удалять.__contains__(self, item):элемент in объект— проверять вхождение.
-
Бухгалтерия (арифметика):
__add__,__sub__,__mul__и куча других.+,-,*,/,//,%,**— всё это можно переопределить. Хочешь, чтобы точки складывались? Реализуй__add__. Хочешь, чтобы матрицы перемножались?__mul__тебе в помощь.
Ну и пример, чтобы не быть пиздаболом:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
"""Для таких же ебланов-разработчиков, как я."""
return f"Point(x={self.x}, y={self.y})"
def __str__(self):
"""Для нормальных людей, которые просто хотят точку увидеть."""
return f"({self.x}, {self.y})"
def __add__(self, other):
"""Вот она, магия! Сложение точек, блядь!"""
if isinstance(other, Point):
return Point(self.x + other.x, self.y + other.y)
raise TypeError("Слышь, мудила, можно складывать только с другой Point!")
def __eq__(self, other):
"""А это чтобы точки на равенство проверять."""
if isinstance(other, Point):
return self.x == other.x and self.y == other.y
return NotImplemented # Говорим: "Я не могу, пусть другой объект попробует"
p1 = Point(1, 2)
p2 = Point(3, 4)
p3 = Point(1, 2)
print(p1) # Вызовет __str__: (1, 2) — красиво
print(repr(p1)) # Вызовет __repr__: Point(x=1, y=2) — точно
p_sum = p1 + p2 # Вызовет __add__ — вот она, перегрузка оператора!
print(f"Сумма: {p_sum}") # (4, 6)
print(f"p1 == p2: {p1 == p2}") # False, вызовет __eq__
print(f"p1 == p3: {p1 == p3}") # True
Вот и вся магия, блядь. Без этих методов твои классы — как немой Герасим: сила есть, а сказать нихуя не может. А с ними — уже полноценный участник питонячьего базара.