Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Если ты пишешь на Python, то наверняка видел в стандартных библиотеках определения методов, обернутых в двойные подчеркивания. Эти «магические» методы образуют многие из полезных интерфейсов, которыми ты постоянно пользуешься, — например, когда получаешь значение по номеру элемента или выводишь что-то через print. Эти методы можно и нужно использовать и в своих программах. Как — сейчас покажу.
Вообще, любой хорошо спроектированный язык определяет набор соглашений и применяет их в своей стандартной библиотеке. Соглашения могут касаться как чисто внешних признаков, вроде синтаксиса названий (CamelCase
, snake_case
), так и поведения объектов. Язык Python в этом смысле — весьма последовательный.
Синтаксис в Python нерасширяем, но зато интерфейсы взаимодействия между объектами хорошо определены и доступны любому разработчику. В отличие от Java в Python нет формальной концепции интерфейсов класса, любой класс может предоставлять любой интерфейс, достаточно определить методы с нужными именами и аргументами и убедиться, что их поведение соответствует ожидаемому.
Поскольку Python динамически типизирован, проверить соответствие класса объекта на этапе компиляции невозможно. Возможности для указания аннотаций типов из Python 3.5 предназначены прежде всего для внешних статических анализаторов и не используются во время выполнения. Явная проверка класса с помощью type()
считается дурным тоном.
В крайнем случае можно использовать isinstance()
— в отличие от type()
эта функция возвращает True
не только для самого класса, но и для всех его потомков. Проверка с помощью type()
сломается при наследовании, именно поэтому люди к ней так плохо относятся.
Интерфейсы объектов определяются так называемыми магическими методами. По соглашению их имена окружаются двойным подчеркиванием. Метод __init__()
, который служит конструктором класса, — пример, известный каждому. Почти каждая стандартная операция, включая форматированный вывод и арифметику, реализуется каким-то магическим способом.
Для демонстрации мы напишем примитивную и медленную реализацию ассоциативного массива на основе списка из кортежей, «идентичную натуральной» в смысле интерфейса.
Реализация будет очень простой — связный список из пар «ключ — значение». Например, эквивалент ассоциативного массива {1: 2, 3: 4}
будет [(1, 2), (3, 4)]
. Она значительно медленнее встроенной: например, поиск значения элемента по ключу будет требовать O(n) операций, в то время как встроенная требует O(1). Для демонстрации, впрочем, вполне сойдет.
Свой класс мы назовем Assoc
. Определим класс и его конструктор:
class Assoc(object):
def __init__(self, contents=[]):
self._contents = contents
Для удобства тестирования мы сделали, чтобы начальное значение можно было передать в конструкторе, вроде Assoc([(1,2), (3,4)])
.
Теперь приступим к магии! Будем считать, что код мы сохранили в файл assoc.py
.
В Python существуют два разных метода для получения строкового представления объектов: __repr__
и __str__
. Различие между ними довольно тонкое, но существенное: __repr__
, по замыслу, должен выдавать допустимое выражение Python, с помощью которого можно создать такой же объект. Это не всегда возможно, поэтому на практике у многих объектов он возвращает просто что-то такое, что позволяет разработчику идентифицировать объект, вроде <Foo object at 0x7f94fe2f22e8>
. Именно он вызывается, если ввести имя переменной в интерактивном интерпретаторе.
Источник: xakep.ru