Dunder методи (скорочення від Double Underscore), також відомі як “магічні методи” або “спеціальні методи”, є однією з корисних можливостей Python. Імена цих методів мають подвійні підкреслення навколо (наприклад __init__, __str__, __getitem__), та дозволяють налаштовувати ваші класи під власні потреби.
Dunder методи – це спеціальні методи, які Python автоматично викликає під час виконання певних операцій над об’єктами (такі як створення нового екземпляра класу, друк об’єкта, виконання арифметичних операцій, та інше). Перевизначаючи або налаштовуючи ці методи у класі, ви можете керувати поведінкою ваших об’єктів у різних контекстах, розширивши таким чином поведінку вбудованих операцій Python, відповідно до ваших конкретних потреб.
Далі буде розглянуто деякі dunder методи з прикладами використання:
- __init__
- __new__
- __str__
- __repr__
- __add__
- _getitem__, __setitem__ та __len__
- __call__
- __enter__ та __exit__
- __del__
- __eq__
- __iter__ та __next__
__init__
Метод __init__ у Python є спеціальним методом класу, який виконує роль конструктора. Його основне завдання – ініціалізація нового екземпляра класу під час його створення (тобто задання початкового стану об’єкта, наприклад ініціалізація його атрибутів). Таким чином, метод __init__ надає можливість контролювати процес налаштування об’єктів класу.
Приклад:
class Rectangle:
def __init__(self, width, height):
if width <= 0 or height <= 0:
raise ValueError('Width and height must be greater than zero')
self.width = width
self.height = height
rectangle_one = Rectangle(5, 10)
print(rectangle_one.width)
# 5
rectangle_two = Rectangle(0, 10)
# ValueError: Width and height must be greater than zeroТакож слід додати, що __init__ не створює об’єкт, цим займається метод __new__. Метод __init__ автоматично викликається вже після створення об’єкту.
__new__
Метод __new__ у Python відповідає за створення нового екземпляра класу. Він автоматично викликається перед методом __init__. Це може бути корисним:
- коли є потреба контролювати процес створення об’єкта класу
- під час реалізації таких шаблонів програмування як Singleton
- коли потрібно перевіряти певні умови перед створенням екземпляра класу
Приклад реалізації шаблону Singleton:
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
obj1 = Singleton()
obj2 = Singleton()
print(obj1 is obj2)
# Виведе True, оскільки це один і той самий об'єктУ цьому прикладі:
- передбачається що змінна _instance зберігає посилання на єдиний екземпляр класу
- у методі __new__, після перевірки змінної _instance на None, якщо екземпляр класу ще не створений, викликається super().__new__(cls) для його створення
- після створення екземпляра класу, на кожний виклик Singleton(), буде повертатись вже збережене посилання на існуючий екземпляр
__str__
Метод __str__ забезпечує зрозуміле для людини рядкове представлення об’єкта. Це корисно для налагодження, ведення журналів (легування) та друку об’єктів.
Приклад:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f'Person name: {self.name}, age: {self.age}'
person = Person('Alice', 30)
print(person)
# Person name: Alice, age: 30Якщо прибрати власну реалізацію методу __str__ у класі Person, то під час друку об’єкта буде використано реалізацію за замовчуванням, котра поверне погано зрозумілий результат:
<__main__.Person object at 0x00000258AAF24F40>__repr__
Метод __repr__ у Python слугує для отримання так званого машиночитабельного строкового представлення об’єкта, яке можна використати для його відтворення. Тобто він повертає рядок, котрий точно описує внутрішню структуру об’єкта. Це може бути корисним для налагодження, документації, тощо.
Приклад:
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})'
point = Point(3, 4)
print(repr(point))
# Point(x=3, y=4)Як бачимо, метод __repr__ повертає рядок, який можна використати для повторного створення потрібного об’єкта класу Point.
__add__
Метод __add__ дозволяє визначити поведінку об’єктів класу при застосуванні до них оператора +, та є прикладом перевантаження операторів.
Приклад:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __str__(self):
return f'Vector({self.x}, {self.y})'
v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2) # Vector(4, 6)__getitem__, __setitem__ та __len__
Ці dunder методи дозволяють працювати з об’єктами користувацьких класів як з контейнерами (список, словник, тощо), використовуючи синтаксис доступу по індексу або ключу (obj[key]), а також обчислювати їх довжину.
Приклад:
class CustomList:
def __init__(self, data=None):
self.data = data if data else []
def __getitem__(self, index):
return self.data[index]
def __setitem__(self, index, value):
self.data[index] = value
def __len__(self):
return len(self.data)
custom_list = CustomList([10, 20, 30])
# Використання __getitem__
print(custom_list[1]) # 20
# Використання __setitem__
custom_list[1] = 50
print(custom_list[1]) # 50
# Використання __len__
print(len(custom_list)) # 3__call__
Визначення методу __call__ у класі, робить його екземпляри доступними для виклику (як звичайної функції).
Приклад:
class Greeter:
def __call__(self, name):
return f'Hello, {name}!'
greet = Greeter()
print(greet('Alice'))
# Hello, Alice!__enter__ та __exit__
Методи __enter__ та __exit__ у Python використовуються для реалізації менеджерів контексту, котрі дозволяють автоматизувати роботу з ресурсами (такими як файли, з’єднання з базою даних, тощо). Ці методи забезпечують виконання певної логіки при вході та виході з блоку with:
- __enter__ виконується напочатку блоку with і повертає ресурс
- __exit__ виконується після виходу з блоку with, та звільняє ресурс (навіть якщо у блоці стався виняток)
Приклад:
class FileManager:
def __init__(self, filename, mode):
self.file = open(filename, mode)
def __enter__(self):
return self.file
def __exit__(self, exc_type, exc_value, traceback):
self.file.close()
# Using
with FileManager('example.txt', 'w') as file:
file.write('Hello, world!')__del__
Метод __del__ у Python викликається під час знищення об’єкта. Він використовується для виконання додаткових дій, перед видаленням об’єкта (наприклад закриття ресурсів, очищення даних, тощо), забезпечуючи таким чином коректне завершення роботи об’єкта.
Приклад:
class Resource:
def __init__(self, name):
self.name = name
def __del__(self):
print(f'Resource {self.name} has been deleted.')
resource = Resource('File')
del resource # Resource File has been deleted.__eq__
Метод __eq__ у Python визначає поведінку оператора == для об’єктів класу. Його використовують щоб задати власну логіку, для порівняння об’єктів класу на рівність.
Приклад:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
if isinstance(other, Point):
return self.x == other.x and self.y == other.y
return False
a = Point(1, 2)
b = Point(1, 2)
c = Point(2, 3)
print(a == b) # True
print(a == c) # False__iter__ та __next__
Методи __iter__ та __next__ у Python використовуються для створення ітераторів – об’єктів, які можна перебирати за допомогою циклу for або інших ітераційних конструкцій:
- __iter__ повертає сам ітератор (зазвичай self)
- __next__ повертає наступний елемент послідовності, або генерує виняток StopIteration, якщо елементи закінчились
Приклад:
class Counter:
def __init__(self, limit):
self.current = 0
self.limit = limit
def __iter__(self):
return self
def __next__(self):
if self.current < self.limit:
self.current += 1
return self.current
raise StopIteration
counter = Counter(3)
for number in counter:
print(number) # 1, 2, 3Інші dunder методи
__lt__, __gt__: перевизначають операції порівняння для об’єктів класу, а саме менше (<) та більше (>).
__hash__: визначає спосіб хешування об’єкта, що може бути корисним, наприклад для використання об’єктів в якості ключів словника.
__bool__: визначає логічне значення об’єкта (True або False), що може знадобитись при використанні об’єктів класу з операторами if, while, тощо.