Это своего рода магия

Python — это язык для большинства проектов по науке о данных и машинному обучению. Однако не каждый специалист по данным обязательно является опытным разработчиком Python, который знает и использует все полезные функции, предлагаемые языком. Это, конечно, понятно, но в то же время и печально. Почему? Потому что знание специфики языка позволяет вам писать код, который менее повторяющийся, более читаемый и более удобный для сопровождения. В целом, при полном использовании языка качество вашего кода станет выше, и, что еще более важно, вы и ваш коллега получите больше удовольствия, что, я думаю, нам всем нравится.

Вот почему я хочу внести свой вклад в повышение уровня ваших знаний Python, чтобы вы могли писать более классный код, возможно, впечатлять своих друзей или коллег и получать больше удовольствия! В частности, в этом посте я хочу поговорить о так называемых dunder-, special- или magic методах. Интересно узнать, что это? Давайте начнем.

Магические методы

Как следует из заголовка, мы поговорим о магических методах Python. Иногда вы также можете прочитать о методах dunder или специальных, которые относятся к одному и тому же. В этом посте я буду использовать термин «магические методы». Так что же это за магические методы?

Основы

Магические методы — это функции, принадлежащие классу. Они могут быть как экземплярами, так и методами класса. Вы можете легко идентифицировать их, так как все они начинаются и заканчиваются двойным подчеркиванием, то есть все они выглядят как __actual_name__. Отсюда и термин dunder, dдвойнойпод счет. Мне понадобилось время, чтобы это выяснить 😃

Вероятно, наиболее важным моментом является то, что магические методы не предназначены для прямого вызова вами! Конечно, вы могли бы сделать это и написать что-то подобное YourClass().__actual_name__(), но, пожалуйста, не надо 😃!

Так как же вызываются магические методы? Они вызываются из определенных действий, которые вы применяете к своему классу или экземпляру вашего класса. Например, вызов str(YourClass()) вызовет магический метод __str__, а YourClass() + YourClass() вызовет __add__, если вы его реализовали.

Чем хороши магические методы? Они позволяют вам писать классы, которые можно использовать вместе со встроенными методами Python. Если вы сделаете это, вы c̶a̶n̶ напишете более читаемый и менее многословный код. Я надеюсь, что это уже становится очевидным из небольшого беглого взгляда в предыдущем абзаце.

Чтобы подчеркнуть, насколько полезны магические методы, и увидеть, как вы можете извлечь выгоду из их использования при машинном обучении или науке о данных, давайте перейдем к конкретному примеру.

Пример: Пользовательский диапазон дат и времени

В качестве примера я хочу показать вам, как с помощью магических методов можно написать что-то похожее на встроенную функцию range.В отличие от встроенной версии пример будет предлагать больше функциональных возможностей и, что более важно, создавать диапазоны даты и времени вместо числовых диапазонов. Конечно, вы могли бы просто использовать Pandas или, может быть, какую-то другую библиотеку, чтобы получить это, но я думаю, что пример служит цели, чтобы способствовать нашему пониманию общей концепции магических методов. Это также позволяет мне показать вам некоторые из наиболее полезных магических методов, особенно при работе с данными.

Просто говорить или писать о коде, не видя реального и работающего кода, скучно, не так ли? Итак, давайте прекратим это делать и посмотрим на реальную реализацию диапазона дат и времени.

Итак, это довольно некоторый код. Если вы сейчас боитесь, потерпите меня, мы прольем на это свет. В целом, я реализовал шесть различных магических методов, которые я объясню ниже.

Первый и, вероятно, самый известный — это метод __init__. Как вы наверняка знаете, этот метод в основном используется для инициализации атрибутов экземпляра вашего класса. Здесь мы устанавливаем начало и конец нашего класса диапазона вместе с размером шага. Это похоже на то, что мы делаем при создании встроенной функции диапазона.

Следующим является метод __iter__. Это, вероятно, самый важный, поскольку он генерирует все элементы, которые являются частью нашего диапазона даты и времени. Эта функция представляет собой так называемую функцию-генератор, которая создает по одному элементу за раз, передает его вызывающей стороне и позволяет вызывающей стороне обработать его. И так до тех пор, пока не достигнет конца диапазона. Вы можете легко определить функцию генератора, увидев ключевое слово yield. Этот оператор приостанавливает функцию, сохраняя все ее состояния, а затем продолжает выполнять последующие вызовы. Это позволяет вам использовать один элемент за раз и работать с ним, не требуя, чтобы каждый элемент находился в памяти.

Отсутствие всего в памяти становится очень удобным, когда либо каждый элемент занимает много памяти, либо когда у вас огромное количество элементов. Например, попробуйте выполнить list(DateTimeRange(datetime(1900,1,1), datetime(2000,1,1)) или лучше не делайте этого, так как это создаст список с 3184617600 записями даты и времени. Слишком большой, извините 😃 Однако с помощью генератора вы можете легко обрабатывать эти элементы один за другим.

Теперь вы видели, что это не список и не кортеж. Однако, чтобы работать с этим классом DateTimeRange так же, как если бы это был список или кортеж, я добавил еще три магических метода, а именно __len__, __contains__ и __getitem__.

С помощью __len__ вы можете узнать количество элементов, входящих в ваш диапазон, вызвав len(my_range) . Это может оказаться очень полезным, например, когда вы перебираете все элементы и хотите знать, сколько элементов вы уже обработали из всех доступных. Он также может сказать вам, эй, мне нужно обработать много данных, пожалуйста, возьмите кофе ☕️.

С помощью __contains__ вы можете проверить, является ли элемент частью вашего диапазона, используя встроенный синтаксис element in my_range. Хорошая вещь в данной реализации заключается в том, что это делается с использованием чистой математики без необходимости сравнивать данный элемент со всеми элементами из диапазона. Это означает, что проверка того, является ли элемент частью вашего диапазона, является операцией с постоянным временем и не зависит от размера фактического экземпляра диапазона. Опять же, это может быть удобно для больших диапазонов, которые мы часто видим при работе с данными.

С помощью __getitem__ вы можете использовать синтаксис индексации для извлечения записей из ваших объектов. Так, например, вы можете получить последний элемент нашего диапазона, написав my_range[-1] . Должен признаться, это, вероятно, наименее полезный метод для конкретного примера. Однако в целом использование __getitem__ позволяет писать очень чистые и читаемые интерфейсы.

Шестой и последний магический метод, который я добавил, это __str__. Этот метод позволяет вам преобразовать экземпляр вашего класса в строку. Это становится очень удобным при вызове print(my_range), так как print должен преобразовать экземпляр в строку и поэтому использует метод __str__.

Заворачивать

В этой статье я познакомил вас с основной концепцией магических методов в Python. Я представил вам первый пример того, как вы можете использовать некоторые из тех методов, которые, на мой взгляд, могут быть удобны при работе с данными. Конечно, есть гораздо больше, например, для создания менеджера контекста или улучшения ваших классов. Я могу порекомендовать вам продолжать исследовать это пространство и продолжать учиться!

Надеюсь, теперь вы готовы создавать волшебные интерфейсы в будущем и, возможно, хвастаться перед друзьями и коллегами 💪.

Спасибо, что подписались на этот пост. Как всегда, не стесняйтесь обращаться ко мне или подписываться на меня для вопросов, комментариев или предложений либо здесь, либо через LinkedIn.