'Магічні' методи

Магічні методи (також відомі як дандер-методи) у Python — це спеціальні методи, які починаються і закінчуються двома символами підкреслення (__). Вони дозволяють змінювати поведінку об'єктів і визначати, як об'єкт реагуватиме на стандартні операції, такі як додавання, віднімання, порівняння, виклик як функції тощо. Ось огляд кількох основних магічних методів і їх використання.

Метод __init__

Метод __init__ використовується для ініціалізації об'єкта. Він викликається автоматично при створенні нового екземпляра класу і дозволяє встановлювати початкові значення атрибутів об'єкта.

class Human:
    def __init__(self, name, age=0):
        self.name = name
        self.age = age

    def say_hello(self):
        return f'Hello! I am {self.name}'

bill = Human('Bill')
print(bill.say_hello())  # Hello! I am Bill
print(bill.age)          # 0

jill = Human('Jill', 20)
print(jill.say_hello())  # Hello! I am Jill
print(jill.age)          # 20

Методи __str__ та __repr__

Метод __str__ відповідає за перетворення об'єкта в рядок, коли викликається функція str(), або коли об'єкт друкується. Метод __repr__ використовується для повернення офіційного строкового представлення об'єкта, яке бажано було б бачити в середовищі розробки або коли об'єкт виводиться в інтерактивному режимі.

class Human:
    def __init__(self, name, age=0):
        self.name = name
        self.age = age

    def __str__(self):
        return f'Hello! I am {self.name}'

bill = Human('Bill')
bill_str = str(bill)
print(bill_str)  # Hello! I am Bill

Методи __getitem__ та __setitem__

Методи __getitem__ та __setitem__ використовуються для доступу до елементів об'єкта за допомогою квадратних дужок, як у списках чи словниках.

class ListedValuesDict:
    def __init__(self):
        self.data = {}

    def __setitem__(self, key, value):
        if key in self.data:
            self.data[key].append(value)
        else:
            self.data[key] = [value]

    def __getitem__(self, key):
        result = str(self.data[key][0])
        for value in self.data[key][1:]:
            result += ", " + str(value)
        return result

l_dict = ListedValuesDict()
l_dict[1] = 'a'
l_dict[1] = 'b'
print(l_dict[1])  # a, b

Функтори: Метод __call__

Метод __call__ дозволяє зробити об'єкт класу викликаємим, як функцію. Це дає змогу викликати об'єкти з аргументами, ніби вони були функціями.

class Adder:
    def __init__(self, add_value):
        self.add_value = add_value

    def __call__(self, value):
        return self.add_value + value

two_adder = Adder(2)
print(two_adder(5))  # 7
print(two_adder(4))  # 6

three_adder = Adder(3)
print(three_adder(5))  # 8
print(three_adder(4))  # 7

Створення власних менеджерів контексту: Методи __enter__ та __exit__

Менеджери контексту використовуються разом із конструкцією with ... as ...: для управління ресурсами, такими як відкриті файли або з'єднання з базою даних. Метод __enter__ викликається на початку блоку with, а метод __exit__ викликається при виході з блоку.

class Session:
    def __init__(self, addr, port=8080):
        self.connected = True
        self.addr = addr
        self.port = port

    def __enter__(self):
        print(f"connected to {self.addr}:{self.port}")
        return self

    def __exit__(self, exception_type, exception_value, traceback):
        self.connected = False
        if exception_type is not None:
            print("Some error!")
        else:
            print("No problem")

localhost_session = Session("localhost")
with localhost_session as session:
    print(session.connected)  # True
print(localhost_session.connected)  # False

Створення ітераторів: Метод __iter__ та __next__

Протокол ітерації у Python реалізується за допомогою методу __iter__, який повертає ітератор, і методу __next__, який повертає наступний елемент послідовності.

class Iterable:
    MAX_VALUE = 10
    def __init__(self):
        self.current_value = 0

    def __next__(self):
        if self.current_value < self.MAX_VALUE:
            self.current_value += 1
            return self.current_value
        raise StopIteration

class CustomIterator:
    def __iter__(self):
        return Iterable()

c = CustomIterator()
for i in c:
    print(i)