Подтвердить что ты не робот

Что может `__init__` делать, что` __new__` не может?

В Python __new__ используется для инициализации неизменяемых типов, а __init__ обычно инициализирует изменяемые типы. Если __init__ были удалены с языка, что больше нельзя было сделать (легко)?

Например,

class A:

    def __init__(self, *, x, **kwargs):
        super().__init__(**kwargs)
        self.x = x


class B(A):

    def __init__(self, y=2, **kwargs):
        super().__init__(**kwargs)
        self.y = y

Можно переписать с помощью __new__ следующим образом:

class A_N:

    def __new__(cls, *, x, **kwargs):
        obj = super().__new__(cls, **kwargs)
        obj.x = x
        return obj


class B_N(A_N):

    def __new__(cls, y=2, **kwargs):
        obj = super().__new__(cls, **kwargs)
        obj.y = y
        return obj

Разъяснение для области вопроса: это не вопрос о том, как используются __init__ и __new__ или в чем разница между ними. Это вопрос о том, что произойдет, если __init__ были удалены с языка. Что-нибудь сломается? Становится ли что-нибудь труднее или невозможнее?

4b9b3361

Ответ 1

Все, что вы можете сделать в __init__, также можно сделать в __new__.

Тогда зачем использовать __init__?
Потому что вам не нужно хранить экземпляр в переменной (obj в вашем примере кода), а затем возвращать его. Вы можете сосредоточиться на том, что вы действительно хотите сделать - ndash; инициализация изменяемого объекта.

Ответ 2

Обратите внимание на разницу между __new__ и __init __

Прежде чем объяснять недостающие функциональные возможности, вернитесь к определению __new__ и __init__:

__ новый __ - это первый шаг создания экземпляра. Он называется первым и отвечает за возврат нового экземпляра вашего класса.

Однако __ init__ ничего не возвращает; он несет ответственность только за инициализируя экземпляр после его создания.

Последствия замены __init__ на __new __

В основном вы потеряете гибкость. Вы получите много семантических головных болей и раздельного разделения инициализатора и конструкции (присоединяя __new__ and init, мы должны присоединиться к конструкции и инициализации в один шаг...). Давайте рассмотрим фрагмент ниже:

class A(object):
    some_property = 'some_value'

    def __new__(cls, *args, **kwargs):
        obj = object.__new__(cls, *args, **kwargs)
        obj.some_property = cls.some_property
        return obj


class B(A):
    some_property = 2

    def __new__(cls, *args, **kwargs):
        obj = super(B, cls).__new__(cls)
        return obj

Последствия перемещения действий __init__ в __new__:

  • Инициализировать B до A: когда вы используете __new__ вместо __init__, ваш первый шаг создания нового экземпляра B вызывает A.__new__ как побочный эффект, вы не можете инициализировать B до инициализации A (доступ и назначение некоторых свойств новому экземпляру B). Использование __init__ дает такую ​​гибкость.

  • Свободный контроль над порядком инициализации: предположим, что у вас есть B_N, унаследованный от двух классов (A_N1, A_N2), теперь вы пропустите управление порядком инициализации нового экземпляра B_N ( какой порядок вы собираетесь инициализировать экземпляры? Это может быть важно... что странно.)

  • Свойства и методы беспорядок: вы пропустили бы доступ к A.some_property (cls был бы равен B при создании нового экземпляра B. Однако возможен прямой доступ к A.some_property, но я предполагаю, что он, по крайней мере, странный для доступа к свойствам внутри класса classt, а не с помощью classmethods).

  • Вы не можете повторно инициализировать существующий экземпляр, не создавая для него новую или специальную логику реализации (благодаря идее @platinhom)

Что может __init__ сделать, что __new__ не может?

В __new__ нет действий, которые не могут быть выполнены в __init__, поскольку действия, которые __init__ выполняет , являются подмножеством действий, которые могут выполняться с помощью __new__.

Интересный момент из Документы Python, Распиловка и рассыпание обычных экземпляров классов # object. getinitargs относительно __init__ может быть полезно:

Когда экземпляр маринованного экземпляра рассыпается, его метод init() обычно не вызывается.

Ответ 3

Per When to use __new__ vs. __init__

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

Также класс класса type, а type.__call__() реализован примерно так, как показано ниже:

def __call__(cls, *args, **kwargs):
    obj = cls.__new__(cls, *args, **kwargs)
    if isinstance(obj, cls):
        obj.__init__(*args, **kwargs)
    return obj

Мы знаем __init__(), только часть части __new__() может сделать что-либо для этого объекта до того, как объект будет возвращен.

В общем случае вам не нужно переопределять __new__, если вы не подклассифицируете неизменяемый тип типа str, int, unicode или кортеж.

Поэтому нехорошо удалять __init__ с языка, и лучше всегда использовать __init__() лучше, чем использовать __new__().

Вот одна история Low-level constructors and __new__().