Содержание статьи
Вообще, любой хорошо спроектированный язык определяет набор соглашений и применяет их в своей стандартной библиотеке. Соглашения могут касаться как чисто внешних признаков, вроде синтаксиса названий (CamelCase
, snake_case
), так и поведения объектов. Язык Python в этом смысле — весьма последовательный.
Синтаксис в Python нерасширяем, но зато интерфейсы взаимодействия между объектами хорошо определены и доступны любому разработчику. В отличие от Java в Python нет формальной концепции интерфейсов класса, любой класс может предоставлять любой интерфейс, достаточно определить методы с нужными именами и аргументами и убедиться, что их поведение соответствует ожидаемому.
INFO
Везде в статье речь идет о Python 3. Python 2.7 уже можно считать мертвым.
Интерфейсы в Python
Поскольку Python динамически типизирован, проверить соответствие класса объекта на этапе компиляции невозможно. Возможности для указания аннотаций типов из Python 3.5 предназначены прежде всего для внешних статических анализаторов и не используются во время выполнения. Явная проверка класса с помощью type()
считается дурным тоном.
В крайнем случае можно использовать isinstance()
— в отличие от type()
эта функция возвращает True
не только для самого класса, но и для всех его потомков. Проверка с помощью type()
сломается при наследовании, именно поэтому люди к ней так плохо относятся.
Интерфейсы объектов определяются так называемыми магическими методами. По соглашению их имена окружаются двойным подчеркиванием. Метод __init__()
, который служит конструктором класса, — пример, известный каждому. Почти каждая стандартная операция, включая форматированный вывод и арифметику, реализуется каким-то магическим способом.
WWW
Полное описание стандартных магических методов можно найти в разделе документации Data Model.
Для демонстрации мы напишем примитивную и медленную реализацию ассоциативного массива на основе списка из кортежей, «идентичную натуральной» в смысле интерфейса.
Делаем свой ассоциативный массив
Реализация будет очень простой — связный список из пар «ключ — значение». Например, эквивалент ассоциативного массива {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>
. Именно он вызывается, если ввести имя переменной в интерактивном интерпретаторе.
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»