11 Магических методов Python, которые должен знать каждый программист
11 Волшебных методов Python, которые должен знать каждый программист
В Python магические методы помогают эмулировать поведение встроенных функций в ваших классах Python. Эти методы имеют двойные подчеркивания в начале и конце (__), и поэтому они также называются методами-дандерами.
Эти магические методы также помогают вам реализовать перегрузку операторов в Python. Вы, наверное, видели примеры этого. Например, использование оператора умножения * с двумя целыми числами дает произведение. А использование его со строкой и целым числом k
дает повторение строки k
раз:
>>> 3 * 412>>> 'code' * 3'codecodecode'
- Эволюция GPT-4 создание инструментов Python Plotly Dashboards с легкостью
- Овладение веб-скрапингом с помощью BeautifulSoup
- Как бы я изучал машинное обучение в 2024 году (если бы мог начать сначала)
В этой статье мы рассмотрим магические методы в Python, создав простой двумерный векторный класс Vector2D
.
Мы начнем с методов, с которыми вы, вероятно, уже знакомы, и постепенно перейдем к более полезным магическим методам.
Давайте начнем писать некоторые магические методы!
1. __init__
Рассмотрим следующий класс Vector2D
:
class Vector2D: pass
После создания класса и создания объекта вы можете добавлять атрибуты так: obj_name.attribute_name = value
.
Однако, вместо того, чтобы вручную добавлять атрибуты к каждому созданному вами экземпляру (что, конечно же, неинтересно!), вам нужен способ инициализировать эти атрибуты при создании объекта.
Для этого вы можете определить метод __init__
. Давайте определим метод __init__
для нашего класса Vector2D
:
class Vector2D: def __init__(self, x, y): self.x = x self.y = yv = Vector2D(3, 5)
2. __repr__
Когда вы пытаетесь проверить или вывести на печать объект, который вы создали, вы видите, что вы не получаете никакой полезной информации.
v = Vector2D(3, 5)print(v)
Output >>> <__main__.Vector2D object at 0x7d2fcfaf0ac0>
Поэтому вы должны добавить строковое представление, строковое представление объекта. Для этого добавьте метод __repr__
вот так:
class Vector2D: def __init__(self, x, y): self.x = x self.y = y def __repr__(self): return f"Vector2D(x={self.x}, y={self.y})"v = Vector2D(3, 5)print(v)
Output >>> Vector2D(x=3, y=5)
Метод __repr__
должен включать все атрибуты и информацию, необходимую для создания экземпляра класса. Метод __repr__
обычно используется в целях отладки.
3. __str__
Метод __str__
также используется для добавления строкового представления объекта. В общем, метод __str__
используется для предоставления информации конечным пользователям класса.
Добавим метод __str__
к нашему классу:
class Vector2D: def __init__(self, x, y): self.x = x self.y = y def __str__(self): return f"Vector2D(x={self.x}, y={self.y})"v = Vector2D(3, 5)print(v)
Вывод >>> Vector2D(x=3, y=5)
Если нет реализации __str__
, то будет использоваться __repr__
. Так что для каждого создаваемого класса необходимо, как минимум, добавить метод __repr__
.
4. __eq__
Затем добавим метод для проверки равенства любых двух объектов класса Vector2D
. Два векторных объекта равны, если у них идентичные координаты x и y.
Теперь создайте два объекта Vector2D
с одинаковыми значениями для x и y и сравните их на равенство:
v1 = Vector2D(3, 5)v2 = Vector2D(3, 5)print(v1 == v2)
Результат – False. По умолчанию сравнение проверяет равенство идентификаторов объекта в памяти.
Вывод >>> False
Добавим метод __eq__
для проверки равенства:
class Vector2D: def __init__(self, x, y): self.x = x self.y = y def __repr__(self): return f"Vector2D(x={self.x}, y={self.y})" def __eq__(self, other): return self.x == other.x and self.y == other.y
Теперь проверка на равенство должна работать ожидаемым образом:
v1 = Vector2D(3, 5)v2 = Vector2D(3, 5)print(v1 == v2)
Вывод >>> True
5. __len__
Встроенная функция Python len()
помогает вычислить длину встроенных итерируемых объектов. Для вектора длина должна возвращать количество элементов, содержащихся вектором.
Поэтому добавим метод __len__
для класса Vector2D
:
class Vector2D: def __init__(self, x, y): self.x = x self.y = y def __repr__(self): return f"Vector2D(x={self.x}, y={self.y})" def __len__(self): return 2v = Vector2D(3, 5)print(len(v))
Все объекты класса Vector2D
имеют длину 2:
Вывод >>> 2
6. __add__
Теперь давайте подумаем о типичных операциях, которые мы выполняем над векторами. Добавим магические методы для сложения и вычитания любых двух векторов.
Если вы попытаетесь сложить два векторных объекта непосредственно, то получите ошибку. Поэтому следует добавить метод __add__
:
class Vector2D: def __init__(self, x, y): self.x = x self.y = y def __repr__(self): return f"Vector2D(x={self.x}, y={self.y})" def __add__(self, other): return Vector2D(self.x + other.x, self.y + other.y)
Теперь можно сложить любые два вектора следующим образом:
v1 = Vector2D(3, 5)v2 = Vector2D(1, 2)result = v1 + v2print(result)
Вывод >>> Vector2D(x=4, y=7)
7. __sub__
Далее давайте добавим метод __sub__
для вычисления разницы между любыми двумя объектами класса Vector2D
:
class Vector2D: def __init__(self, x, y): self.x = x self.y = y def __repr__(self): return f"Vector2D(x={self.x}, y={self.y})" def __sub__(self, other): return Vector2D(self.x - other.x, self.y - other.y)
v1 = Vector2D(3, 5)v2 = Vector2D(1, 2)result = v1 - v2print(result)
Вывод >>> Vector2D(x=2, y=3)
8. __mul__
Мы также можем определить метод __mul__
для определения умножения между объектами.
Давайте реализуем его для выполнения следующих операций:
- Скалярное умножение: умножение вектора на скаляр и
- Скалярное произведение: скалярное произведение двух векторов.
class Vector2D: def __init__(self, x, y): self.x = x self.y = y def __repr__(self): return f"Vector2D(x={self.x}, y={self.y})" def __mul__(self, other): # Скалярное умножение if isinstance(other, (int, float)): return Vector2D(self.x * other, self.y * other) # Скалярное произведение elif isinstance(other, Vector2D): return self.x * other.x + self.y * other.y else: raise TypeError("Unsupported operand type for *")
Теперь рассмотрим несколько примеров для демонстрации метода __mul__
в действии.
v1 = Vector2D(3, 5)v2 = Vector2D(1, 2)# Скалярное умножениеresult1 = v1 * 2print(result1) # Скалярное произведениеresult2 = v1 * v2print(result2)
Вывод >>>Vector2D(x=6, y=10)13
9. __getitem__
Магический метод __getitem__
позволяет индексировать объекты и получать доступ к атрибутам или срезам атрибутов с использованием квадратных скобок [ ].
Для объекта v
класса Vector2D
:
v[0]
: координата xv[1]
: координата y
Если вы попытаетесь получить доступ по индексу, вы получите ошибку:
v = Vector2D(3, 5)print(v[0],v[1])
---------------------------------------------------------------------------TypeError Traceback (most recent call last) in ()----> 1 print(v[0],v[1])TypeError: 'Vector2D' object is not subscriptable
Давайте реализуем метод __getitem__
:
class Vector2D: def __init__(self, x, y): self.x = x self.y = y def __repr__(self): return f"Vector2D(x={self.x}, y={self.y})" def __getitem__(self, key): if key == 0: return self.x elif key == 1: return self.y else: raise IndexError("Index out of range")
Теперь вы можете получить доступ к элементам с помощью индексов, так как показано ниже:
v = Vector2D(3, 5)print(v[0]) print(v[1])
Вывод >>>35
10. __call__
С помощью реализации метода __call__
вы можете вызывать объекты, как если бы они были функциями.
В классе Vector2D
мы можем реализовать __call__
, чтобы масштабировать вектор на заданный коэффициент:
class Vector2D: def __init__(self, x, y): self.x = x self.y = y def __repr__(self): return f"Vector2D(x={self.x}, y={self.y})" def __call__(self, scalar): return Vector2D(self.x * scalar, self.y * scalar)
Так что если вы сейчас вызовете 3, вы получите вектор, умноженный на 3:
v = Vector2D(3, 5)result = v(3)print(result)
Output >>> Vector2D(x=9, y=15)
11. __getattr__
Метод __getattr__
используется для получения значений конкретных атрибутов объектов.
В этом примере мы можем добавить магический метод __getattr__
, который вызывается для вычисления величины (L2-нормы) вектора:
class Vector2D: def __init__(self, x, y): self.x = x self.y = y def __repr__(self): return f"Vector2D(x={self.x}, y={self.y})" def __getattr__(self, name): if name == "magnitude": return (self.x ** 2 + self.y ** 2) ** 0.5 else: raise AttributeError(f"Объект 'Vector2D' не имеет атрибута '{name}'")
Проверим, работает ли это ожидаемым образом:
v = Vector2D(3, 4)print(v.magnitude)
Output >>> 5.0
Заключение
Вот и всё для этого учебного пособия! Надеюсь, вы научились добавлять магические методы в свой класс, чтобы эмулировать поведение встроенных функций.
Мы рассмотрели некоторые из самых полезных магических методов. Однако это не исчерпывающий список. Чтобы более глубоко понять, создайте собственный класс на Python и добавьте магические методы в зависимости от необходимой функциональности. Продолжайте писать код!
[Bala Priya C](https://twitter.com/balawc27) – разработчик и технический писатель из Индии. Ей нравится работать в области математики, программирования, науки о данных и контента. Ее интересы и экспертиза включают DevOps, науку о данных и обработку естественного языка. Она любит чтение, письмо, кодирование и кофе! В настоящее время она работает над обучением и делится своими знаниями с сообществом разработчиков, создавая учебники, руководства, мнения и многое другое.