Шаг 146.
Python: сборник рецептов.
Классы и объекты. Создание экземпляра без вызова init

    На этом шаге мы рассмотрим способ решения указанной задачи.

Задача

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

Решение

    "Голый" экземпляр может быть создан с помощью прямого вызова метода класса __new__(). Например, рассмотрите такой класс:

>>> class Date:
	def __init__(self, year, month, day):
		self.year = year
		self.month = month
		self.day = day

		
>>> 

    Вот как вы можете создать экземпляр Date без вызова init():

>>> d = Date.__new__(Date)
>>> d
<__main__.Date object at 0x000002E72EA19880>
>>> d.year
Traceback (most recent call last):
  File "<pyshell#4>", line 1, in <module>
    d.year
AttributeError: 'Date' object has no attribute 'year'
>>> 

    Как вы можете видеть, получившийся экземпляр не инициализирован. Поэтому теперь установка нужных переменных экземпляра лежит на вашей ответственности. Например:

>>> data = {'year': 2012, 'month': 8, 'day': 29}
>>> for key, value in data.items():
	setattr(d, key, value)
>>> d.year
2012
>>> d.month
8
>>> 


Обсуждение

    Задача обхода метода __init__() иногда возникает, когда экземпляры создаются нестандартным путем, таким как десериализация данных или реализация метода класса, определенного в качестве альтернативного конструктора. Например, в показанном классе Date некто может определить альтернативный конструктор today(), как показано тут:

from time import localtime 

class Date:
    def __init__(self, year, month, day):
        self.year = year 
        self.month = month 
        self.day = day

    @classmethod 
    def today(cls):
        d = cls.__new__(cls)
        t = localtime() 
        d.year = t.tm_year 
        d.month = t.tm_mon 
        d.day = t.tm_mday 
        return d

    При десериализации данных из JSON вы можете получить словарь, подобный этому:

data = {'year': 2012, 'month': 8, 'day': 29}

    Если вы хотите превратить его в экземпляр Date, просто используйте показанный в решении прием.

    При создании экземпляров нестандартным путем обычно лучше не делать слишком много предположений по поводу их реализации. В общем случае лучше не писать код, который напрямую манипулирует внутренним словарем __dict__, если вы не знаете точно, что он гарантированно будет определен. В противном случае код поломается, если класс использует __slots__, свойства, дескрипторы или другие продвинутые приемы. Если же вы будете использовать для присваивания значений setattr(), то ваш код будет настолько универсальным, насколько это возможно.

    На следующем шаге мы рассмотрим расширение классов с помощью миксин (mixins).




Предыдущий шаг Содержание Следующий шаг