В большинстве известных языков ОО выражение, такое как SomeClass(arg1, arg2)
, выделит новый экземпляр, инициализирует атрибуты экземпляра, а затем вернет его.
В большинстве известных ОО-языков часть «инициализировать атрибуты экземпляра» можно настроить для каждого класса, определив конструктор , который в основном представляет собой просто блок кода, который работает с новым экземпляром (используя аргументы, предоставленные выражению конструктора), чтобы установить любые начальные условия. В Python это соответствует методу класса __init__
.
Python's __new__
- это не что иное, как и подобная настройка для каждого класса части "allocate a new instance". Это, конечно, позволяет вам делать необычные вещи, такие как возврат существующего экземпляра, а не выделение нового. Так что в Python мы не должны думать об этой части как об обязательном распределении; все, что нам нужно, это то, что __new__
придумывает подходящий экземпляр откуда-то.
Но это все еще только половина работы, и у системы Python нет возможности узнать, что иногда вы хотите запустить другую половину работы (__init__
) впоследствии, а иногда нет. Если вы хотите такого поведения, вы должны сказать об этом явно.
Часто вы можете выполнить рефакторинг, чтобы вам требовался только __new__
, или вам не нужен __new__
, или __init__
ведет себя по-другому на уже инициализированном объекте. Но если вы действительно хотите, Python действительно позволяет вам переопределить «работу», так что SomeClass(arg1, arg2)
не обязательно вызывает __new__
, за которым следует __init__
. Для этого вам нужно создать метакласс и определить его метод __call__
.
Метакласс - это просто класс класса. А метод класса __call__
контролирует, что происходит, когда вы вызываете экземпляры класса. Таким образом, метод metaclass '__call__
контролирует, что происходит при вызове класса; то есть он позволяет переопределить механизм создания экземпляра от начала до конца . Это уровень, на котором вы можете наиболее элегантно реализовать совершенно нестандартный процесс создания экземпляра, такой как шаблон синглтона. Фактически, имея менее 10 строк кода, вы можете реализовать метакласс Singleton
, который даже не требует от вас использовать __new__
для всех и может превратить в любой иначе обычный класс в синглтоне, просто добавив __metaclass__ = Singleton
!
class Singleton(type):
def __init__(self, *args, **kwargs):
super(Singleton, self).__init__(*args, **kwargs)
self.__instance = None
def __call__(self, *args, **kwargs):
if self.__instance is None:
self.__instance = super(Singleton, self).__call__(*args, **kwargs)
return self.__instance
Однако это, вероятно, более глубокая магия, чем на самом деле оправдано для этой ситуации!