`__new__` и` __init__` для класса и объекта - PullRequest
0 голосов
/ 28 января 2019

В 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 или в его метаклассе?

Ответы [ 3 ]

0 голосов
/ 28 января 2019

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

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

0 голосов
/ 28 января 2019

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

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

__new__() - статический метод (в специальном случае, поэтому вам не нужно объявлять его как таковой)

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

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

>>> 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__()

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

>>> cls.__init__(self)
0 голосов
/ 28 января 2019

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 to create class и initialize class.
Это метапрограммирование PythonПредлагаю прочитать девятую главу Python Cookbook.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...