Dunder-методы: зачем они нужны и что могут

Рассказываем, что такое Dunder-методы, которые в Python определяются с двумя подчёркиваниями, зачем они нужны и как их использовать.

7К открытий13К показов
Dunder-методы: зачем они нужны и что могут

Абсолютно все питонисты рано или поздно спотыкаются о методы с двойными подчеркиваниями. Зачем они вообще нужны? Почему без них не обойтись? Кто это придумал? В чем отличие от классических методов без подчеркиваний? Почему они не везде? Если вы тоже задаетесь такими вопросами, то вашему вниманию обзор на некоторые из самых популярных методов с двойным подчеркиванием. В Python они определяют кастомные свойства и поведение объектов.

ВРЕЗКА. Стоит отличать их от магических методов IPython, которые делают работу в Google Colaboratory / Jupyter Notebook удобнее. Если вы хотите познакомиться с последними поближе, прочитайте мою статью “Топ самых полезных магических команд для завсегдатаев Python”.

Например, когда вы выполняете операции, такие как сложение (+), вычитание (-), умножение (*), и деление (/), Python автоматически вызывает соответствующие “магические” методы (__add__, __sub__, __mul__, и __div__ соответственно).

Слово «dunder» здесь – не ругательство, оно отсылает к двойному подчеркиванию. Два underscore нужны только для того, чтобы предотвратить конфликт имен с другими методами, реализованными ничего не подозревающими программистами. И внедрил их, конечно, создатель языка Гвидо ван Россум.

Да кому нужны эти Dunder-методы?

Dunder-методы: зачем они нужны и что могут 1

Чтобы понять, зачем этот класс методов выделен в отдельную группу, рассмотрим пример из треда на Stack Overflow. Пользователь Greg Beech спрашивает, зачем вообще нужны такие усложнения, если вместо __len__ можно использовать просто len()? Замечательный сниппет от Mangu Singh Rajpurohit раз и навсегда отвечает на этот вопрос, на мой взгляд:

«Представьте, что у вас есть два словаря, и вы хотите их сложить:

			dict1 = {1 : "ABC"}
dict2 = {2 : "EFG"}

dict1 + dict2
		

При исполнении этого кода будет вызываться ошибка:

			TypeError: unsupported operand type(s) for +: 'dict' and 'dict'
		

Ошибка TypeError гласит, что словари складывать нельзя. А ведь нам так надо! Так что давайте добавим классу “словарь” этот функционал. Точнее, инициируем дочерний класс AddableDict:

			class AddableDict(dict):

    def __add__(self, otherObj):
        self.update(otherObj)
        return AddableDict(self)


dict1 = AddableDict({1 : "ABC"})
dict2 = AddableDict({2 : "EFG"})

print (dict1 + dict2)
		

И вуаля! Словари складываются:

			{1: 'ABC', 2: 'EFG'}
		

Некоторые интересные Dunder-методы

1. __init__:

Если у вас есть определенные классы в Python, вы обязательно встретитесь с методом __init__. Он отвечает за инициализацию экземпляра класса, поэтому именно в нем вы обычно устанавливаете его неотъемлемые атрибуты – например, длина ребра квадрата:

			class Square:
    def __init__(self, side_length):
        """
        Чтобы создать квадрат (Square), нам нужно знать длину его         стороны, чтобы позднее передать это значение как аргумент:        Square(1). Чтобы убедиться, что сущность знает свою длину         стороны, сохраним ее так:
        """
        self.side_length = side_length

sq = Square(1)
		

2. __call__:

Этот метод позволяет создать так называемые «вызываемые» объекты, то есть их можно вызывать как функции.

			class CallableObject: 
    def __call__(self, x): 
        return x ** 2 
    
    callable_object = CallableObject() 
    print(callable_object(5)) # выводит 25
		

3. __getitem__ и __setitem__:

Методы позволяют обращения к объекту по индексу или ключу, как если бы это был список или словарь.

			class MyDictionary:
    # выводит "value"
    def __init__(self): self.dictionary = {} 
    def __getitem__(self, key): return self.dictionary[key] 
    def __setitem__(self, key, value): self.dictionary[key] = value 


my_dict = MyDictionary() 
my_dict["test"] = "value" 
print(my_dict["test"])
		

4. __iter__ и __next__:

Эти методы позволяют создавать итерируемые объекты, которые можно использовать в цикле for.

			class IterableObject:
    def __init__(self, max): 
        self.max = max 
        self.current = 0
        

def __iter__(self): 
    return self 


def __next__(self): 
    if self.current < self.max: 
        result = self.current 
        self.current += 1 
        return result 
    else: 
        raise StopIteration 

iterable_object = IterableObject(5) 
for i in iterable_object:      print(i)  # выводит числа от 0 до 4
		

5. __enter__ и __exit__:

Эти методы используются для создания контекстных менеджеров (то есть объектов, которые можно использовать в блоке with), и позволяют управлять ресурсами, которые нужно освободить после использования.

			class MyContextManager: 
    def __init__(self): 
        print("Инициализируем блок") 
        
    def __enter__(self): 
        print("Входим в блок") 
        return self 
    
    def __exit__(self, exc_type, exc_val, exc_tb): 
        print("Выходим из блока") 
with MyContextManager():              pass
		

При входе и выходе из блока with будут выведены соответствующие сообщения.

И небольшая шпаргалка

Вашему вниманию перечень несложных методов, которые могут пригодиться, если вы в целом готовы впустить Dunder’ы в свой код:

Dunder-методы: зачем они нужны и что могут 2

Заключение

Если честно, у меня ушел не один год, чтобы понять и принять концепцию Dunder-методов. Из-за информационной перегрузки мозг часто стремился избегать дополнительных сложностей — и магические методы как раз попали в эту категорию. Лишь исчерпав на практике классические возможности тех или иных объектов, я вернулась к этой теме с помощью StackOverflow.

Так что не корите себя, если эту тему хочется поначалу увидеть и тут же забыть: не все в Python подходит новичкам, на некоторые вещи нужны опыт и спрос. Но освоившись с такой крутой фишкой (и еще десятком таких), вы получите преимущество в конкурентной борьбе за интереснейшие проекты, лучшие условия труда и больший оффер.

Как часто вы используете Dunder-методы?
Каждый божий день
Реже раза в неделю
Реже раза в месяц
Другое (напишите в комментариях)
Никогда не использовал
Следите за новыми постами
Следите за новыми постами по любимым темам
7К открытий13К показов