Быстрый в изучении - мощный в программировании
>> Telegram ЧАТ для Python Программистов

Свободное общение и помощь советом и решением проблем с кодом! Заходите в наш TELEGRAM ЧАТ!

>> Python Форум Помощи!

Мы создали форум где отвечаем на все вопросы связанные с языком программирования Python. Ждем вас там!

>> Python Канал в Telegram

Обучающие статьи, видео и новости из мира Python. Подпишитесь на наш TELEGRAM КАНАЛ!

Гибкое создание объектов в Python с помощью метода __new__

Мы часто называем __init__ конструктором, но это только потому, что позаимствовали терминологию из других языков. На самом деле, конструирует экземпляр специальный метод __new__.

Это метод класса (однако он обрабатывается особым образом, поэтому декоратор @classmethod не используется), и возвращать он должен экземпляр. Этот экземпляр затем передается в качестве первого аргумента self методу __init__.

Поскольку __init__ при вызове уже получает экземпляр, что-то возвращать ему запрещено, по существу, метод __init__ является инициализатором. Настоящий конструктор - это метод __new__, но мы о нем редко вспоминаем, потому что реализации, унаследованной от класса object, обычно достаточно.

томского государственного университета систем управления и радиоэлектроники

Описанный только что путь - от __new__ к __init__ - самый распространенный, но не единственный. Метод __new__ может возвращать и экземпляр другого класса, если такое происходит, то интерпретатор не вызывает __init__.

Иными словами, процесс построения объекта в Python можно описать следующим псевдокодом:

# псевдокод конструирования объекта
def make_object(the_class, some_arguments):
    new_object = the_class.__new__(some_arguments)
    if isinstance(new_object, the_class):
        the_class.__init__(new_object, some_arguments)
    return new_object
# следующие строки кода приблизительно эквивалентны
obj = Foo("test")
obj = make_object(Foo, "test")

В следующем примере показан класс FrozenJSON.

from collections import abc
class FrozenJSON:
    def __new__(cls, arg):
        if isinstance(arg, abc.Mapping):
            return super().__new__(cls)
        elif isinstance(arg, abc.MutableSequence):
            return [cls(item) for item in arg]
        else:
            return arg
    def __init__(self, mapping):
        self.__data = {}
        for key, value in mapping.items():
            if iskeyword(key):
                key += '_'
            self.__data[key] = value
    def __getattr__(self, name):
        if hasattr(self.__data, name):
            return getattr(self.__data, name)
        else:
            return FrozenJSON(self.__data[name])