`__new__` и `__init__` для класса и объекта

Почему в Python 3 при определении подкласса вам нужно использовать cls в качестве первого аргумента __new__, но не использовать self в качестве первого аргумента __init__?

Пример:

class MyClass(object):
    def __new__(cls, *args, **kwargs):
        return super(MyClass, cls).__new__(cls, *args, **kwargs) # with `cls`
    def __init__(self, *args, **kwargs):
        return super(MyClass, self).__init__(*args, **kwargs) # without `self`

Когда я сравнил эти функции, я еще больше запутался:

>>> cls = object
>>> self = cls()
>>> cls.__new__ is self.__new__
True
>>> cls.__init__ is self.__init__
False
>>> self.__init__()
>>> cls.__init__()
Traceback (most recent call last): ...

Итак, в чем разница между __new__ и __init__, стоящая за этими результатами? Какие методы являются связанными, а какие бесплатными? Почему вы можете вызвать self.__init__(), но не cls.__init__()? Является ли cls.__init__ методом, определенным в самом cls или в его метаклассе?

Возможный дубликат Что может `__init__` сделать, чего не может `__new__`?

TheExorcist 28.01.2019 04:54

@TheExorcist: Не дубликат. Этот вопрос совершенно другой.

user2357112 supports Monica 28.01.2019 04:55

Я также вижу, что вы используете new и init одинаково, тогда как вы можете увидеть разницу. Не используйте его для инициализации, а измените способ инициализации.

TheExorcist 28.01.2019 05:01

Учитывая, что это не тривиальная тема, и вы приложили некоторые усилия, чтобы поиграть с ней и понять, что происходит, +1

Mad Physicist 28.01.2019 05:25
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
4
1 213
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

cls обозначает сам класс, а self обозначает сам объект. Это всего лишь условности.
Метод __new__ называется до, объект создается, фактически __new__ должен создать объект и вернуть его. Следовательно, для создания объекта требуется класс. После этого __init__ вызывается к объекту инициализировать, поэтому ему нужен объект в качестве первого аргумента.

Например:

class MyClass:
    def __new__(cls, *args, **kwargs):
        # cls == MyClass
        return super().__new__(cls, *args, **kwargs)
        # cls here is because __new__ is staticmethods so you have to pass the cls explicitly

        # You can't use cls() here because it will call this methods again and again
        # causing recusion error

    def __init__(self, *args, **kwargs):
        # Here self is the instance(or object) of MyClass
        # So you can initialize it by self.xxx
        self.xxx = 'xxx'

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

cls.__init__(self)

Все в Python является объектом, включая сам класс. Итак, для класса у него есть свои собственные __new__ и __init__, которые используются metaclass для класса Создайте и класса инициализировать.
. Это метапрограммирование Python, предлагаю прочитать девятую главу Python Cookbook.

Ответ принят как подходящий

Самая большая часть картины, которую вы, вероятно, упускаете, это то, что __new__ является статическим методом, особый случай будет таковым, даже если вы не используете декоратор @staticmethod.

При вызове метода через super()super() выполняет такую ​​же привязку аргументов, которая обычно выполняется для такого метода (с использованием протокол дескриптора). Для статического метода, такого как __new__, это означает, что никакие аргументы не связываются автоматически, поэтому cls должен передаваться явно. Для метода экземпляра, такого как __init__, это означает, что self привязывается автоматически, поэтому вам не нужно передавать self в super().__init__.

Мне было интересно, почему __new__ сделан статическим, а не методом класса, в то время как вам все равно нужно передать аргумент класса.

Cyker 28.01.2019 05:35

@Cyker: со статическим методом вы можете делать такие вещи, как object.__new__(Whatever). Было бы гораздо более неудобно воспроизводить эту функциональность с помощью метода класса.

user2357112 supports Monica 28.01.2019 05:39

Но зачем object.__new__(Whatever), если можно Whatever.__new__()?

Cyker 28.01.2019 05:42

@Cyker: Потому что вы хотите использовать метод object__new__ вместо метода Whatever.

user2357112 supports Monica 28.01.2019 05:44

Тогда почему вы не хотите использовать метод object__init__ вместо метода Whatever?

Cyker 28.01.2019 10:34

@Cyker: object.__new__(Whatever) вообще не вызывает никаких методов __init__. Если вы хотите вызвать object.__init__, вы можете вызвать его вручную для результирующего объекта, но object.__init__ не выполняет никакой инициализации. В любом случае обычная причина вызова надкласса __new__ вручную вне подкласса __new__ заключается в том, что подкласс __new__ отказался бы создавать объект.

user2357112 supports Monica 28.01.2019 17:19

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

Согласно документам:

__new__() is a static method (special-cased so you need not declare it as such)

__init__, с другой стороны, является правильным методом экземпляра. Кстати, его можно вызывать несколько раз в одном и том же экземпляре.

Этого должно быть достаточно, чтобы объяснить вашу терминальную сессию:

>>> cls = object
>>> self = cls()

Вы только что позвонили в object.__call__, что по сути делает

self = cls.__new__()
if isinstance(self, cls):
    cls.__init__(self)
return self

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

>>> cls.__new__ is self.__new__
True

__new__ — это статический метод, поэтому попытка привязать его к экземпляру ничего не дает: он остается методом класса. По той же причине вы должны явно передавать cls при вызове super().__new__: это та же свободная функция, не привязанная ни к классу, ни к экземпляру.

 >>> cls.__init__ is self.__init__
 False

Это не только не одно и то же, но и типы их разные. cls.__init__ — обычная функция. self.__init__ — это связанный метод, в котором отсутствует первый параметр cls.__init__.

>>> self.__init__()

Это уже вызывалось, но для object это не работает, вы можете вызывать столько раз, сколько захотите. Обратите внимание, что первый параметр не передается, так как это связанный метод.

>>> cls.__init__()

Это вызывает необработанную функцию __init__, которая требует, чтобы был передан параметр self. Поскольку вы этого не делаете, он поднимается. Попробуйте это вместо этого:

>>> cls.__init__(self)

Если self.__init__() вызывает метод __init__, определенный в type(self), почему cls.__init__() не вызывает метод __init__, определенный в type(cls)? Разве объект класса не является также объектом?

Cyker 28.01.2019 06:14

@Сайкер. Интересный момент. Поскольку метод не является дескриптором данных, любой атрибут экземпляра с таким же именем переопределит его. Если вы выполните self.__init__ = some_other_function, вы увидите, что при выполнении self.__init__ вызывается другая функция. Если вы не определили __init__ в классе, он было бы использует тот, что в метаклассе. Интересно отметить, что вы не можете переопределить подобные методы dunder на уровне экземпляра, потому что методы dunder всегда вызываются как type(self).__dunder__(self, ...) при использовании в качестве операторов.

Mad Physicist 28.01.2019 06:20

Я думаю, что если я не определю __init__ в классе, он будет использовать его в своем суперклассе. Только если у суперкласса нет этого метода, он будет использовать метод из своего метакласса. Но object.__init__ существует всегда, поэтому тот, что находится в его метаклассе, использоваться не будет.

Cyker 28.01.2019 06:38

@ Сайкер прав. Object не имеет суперкласса, что упрощает этот случай.

Mad Physicist 28.01.2019 06:45

Другие вопросы по теме