Что такое инстанцирование в программировании
Перейти к содержимому

Что такое инстанцирование в программировании

Инстанцирование (класса) — что это в программировании

melisa's picture

Инстанцирование (от слова instance — т.е. «сущность») — это создание экземпляра класса, т.е. объекта.

Пример инстанцирования класса

Строка на С-подобном синтаксисе (напр. PHP), в которой происходит инстанцирование класса MyClass:

Инстанцирование в Python

Какой метод вызывается первым при этом вызове Foo? Большинство новичков, да и, возможно, немало опытных питонистов тут же ответят: «метод __init__». Но если внимательно приглядеться к сниппетам выше, вскоре станет понятно, что такой ответ неверен.

__init__ не возвращает никакого результата, а Foo(1, y=2), напротив, возвращает экземпляр класса. К тому же __init__ принимает self в качестве первого параметра, чего не происходит при вызове Foo(1, y=2). Создание экземпляра происходит немного сложнее, о чём мы и поговорим в этой статье.

Порядок создания объекта

Инстанцирование в Python состоит из нескольких стадий. Понимание каждого шага делает нас чуть ближе к пониманию языка в целом. Foo — это класс, но в Питоне классы это тоже объекты! Классы, функции, методы и экземпляры — всё это объекты, и всякий раз, когда вы ставите скобки после их имени, вы вызываете их метод __call__. Так что Foo(1, y=2) — это эквивалент Foo.__call__(1, y=2). Причём метод __call__ объявлен в классе объекта Foo. Какой же класс у объекта Foo?

Так что класс Foo — это экземпляр класса type и вызов метода __call__ последнего возвращает класс Foo. Теперь давайте разберём, что из себя представляет метод __call__ класса type. Ниже находятся его реализации на C в CPython и в PyPy. Если надоест их смотреть, прокручивайте чуть дальше, чтобы найти упрощённую версию:

CPython

Если забыть про всевозможные проверки на ошибки, то коды выше примерно эквивалентны такому:

__new__ выделяет память под «пустой» объект и вызывает __init__, чтобы его инициализировать.

  1. Foo(*args, **kwargs) эквивалентно Foo.__call__(*args, **kwargs).
  2. Так как объект Foo — это экземпляр класса type, то вызов Foo.__call__(*args, **kwargs) эквивалентен type.__call__(Foo, *args, **kwargs).
  3. type.__call__(Foo, *args, **kwargs) вызывает метод type.__new__(Foo, *args, **kwargs), возвращающий obj.
  4. obj инициализируется при вызове obj.__init__(*args, **kwargs).
  5. Результат всего процесса — инициализированный obj.

Кастомизация

Теперь давайте переключим наше внимание на __new__. Этот метод выделяет память под объект и возвращает его. Вы вольны кастомизировать этот процесс множеством разных способов. Следует отметить, что, хотя __new__ и является статическим методом, вам не нужно объявлять его используя @staticmethod: интерпретатор обрабатывает __new__ как специальный случай.

Распространённый пример переопределения __new__ — создание Синглтона:

Обратите внимание, что __init__ будет вызываться каждый раз при вызове Singleton(), поэтому следует соблюдать осторожность.

Другой пример переопределения __new__ — реализация паттерна Борг («Borg»):

Учтите, что хотя примеры выше и демонстрируют возможности переопределения __new__, это ещё не значит что его обязательно нужно использовать:

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

— Арион Спрэг, Хорошо забытое старое в Python

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

Лекция 8. Шаблоны

Шаблоны (templates) — это механизм, позволяющий обобщать функции и классы для работы с различными типами данных.

Шаблоны обеспечивают непосредственную поддержку обобщенного программирования (т.е. c использованием типов в качестве параметров)

Шаблон зависит только от тех свойств параметра-типа, которые он явно использует

Существуют шаблоны функций и классов

Инстанцирование

Процесс порождения функции или класса из шаблона называется инстанцированием

Процесс генерации объявления класса по шаблону класса и аргументу шаблона

Версия шаблона для конкретного аргумента шаблона называется специализацией

Генерация версий шаблона — задача компилятора

1.1 Шаблоны функций

Для создания шаблона используется ключевое слово template. Также указывается пока неопределенный тип T.

Рассмотрим пример шаблонной функции swap для обмена значений двух переменных

Для получения функции проведем инстанцирование

Еще один пример: функция сортировки

Пример шаблона с целочисленным параметром

Примеры использования шаблонов

1.2 Шаблоны классов

Аналогично функциям можно создавать шаблоны классов. Рассмотрим пример стека:

Воспользоваться шаблоном класса можно так

Описание конструктора и деструктора шаблонного класса

Описание методов push и pop

Описание методов определения размера стека

Примеры инстанцирования

Рассмотрим примеры использования шаблона стека

1.3 Параметры шаблонов

Параметры шаблона

У шаблонов могут быть параметры различных типов

Инстанцирование выполняется с указанием значения параметра

В шаблонах допускается использование различных видов параметров

Если в шаблоне класса или функции необходимо использовать один и тот же шаблон, но с разными параметрами, то используются параметры-шаблоны. Например:

Перегрузка шаблонов
Специализация шаблонов
Явное инстанцирование

Явное инстанцирование используется

  1. Если инстанцирование шаблонов отнимает слишком много времени
  2. Если порядок компиляции должен быть абсолютно предсказуем

2 Специализация шаблонов

Для чего нужна специализация шаблонов? Для того чтобы задать шаблон для отдельного значения параметра (типа или значения).

В приведённом примере создаётся шаблон функции сравнения двух элементов одного типа. Так можно сравнивать любые числа, но не строки.

Для строк создаётся специализация.

2.1 Класс Bag

Шаблон класса Bag

В следующем примере приводится шаблон класса Bag, который является

динамическим контейнером элементов и его специализация,

позволяющая задавать элементы не по значению, а по указателю.

2.2 Пример с наследованием

Мы можем использовать специализацию при наследовании.

Рассмотрим следующую ситуацию. Допустим мы хотим создать класс, услуги которого базируются на сходных по назначению, но отличных в их реализации классах BaseA и BaseB. Если оба базовых класса обладают схожим интерфейсом, то логично оформить наш класс в виде шаблона:

если разработчику необходима информация о базовых классах объектов, основанных на шаблоне Derived. Как ее получить?

Следующий метод основан на введении вспомогательного шаблона класса (или структуры) BaseClassInstance, не содержащего ничего, кроме статической константы типа BaseClass, и специализированного для разных фактических типов базовых классов.

Описываем шаблон класса-наследника, в который помещается метод GetBaseClass

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *