Почему в 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 или в его метаклассе?
@TheExorcist: Не дубликат. Этот вопрос совершенно другой.
Я также вижу, что вы используете new и init одинаково, тогда как вы можете увидеть разницу. Не используйте его для инициализации, а измените способ инициализации.
Учитывая, что это не тривиальная тема, и вы приложили некоторые усилия, чтобы поиграть с ней и понять, что происходит, +1






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: со статическим методом вы можете делать такие вещи, как object.__new__(Whatever). Было бы гораздо более неудобно воспроизводить эту функциональность с помощью метода класса.
Но зачем object.__new__(Whatever), если можно Whatever.__new__()?
@Cyker: Потому что вы хотите использовать метод object__new__ вместо метода Whatever.
Тогда почему вы не хотите использовать метод object__init__ вместо метода Whatever?
@Cyker: object.__new__(Whatever) вообще не вызывает никаких методов __init__. Если вы хотите вызвать object.__init__, вы можете вызвать его вручную для результирующего объекта, но object.__init__ не выполняет никакой инициализации. В любом случае обычная причина вызова надкласса __new__ вручную вне подкласса __new__ заключается в том, что подкласс __new__ отказался бы создавать объект.
Основная цель __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)? Разве объект класса не является также объектом?
@Сайкер. Интересный момент. Поскольку метод не является дескриптором данных, любой атрибут экземпляра с таким же именем переопределит его. Если вы выполните self.__init__ = some_other_function, вы увидите, что при выполнении self.__init__ вызывается другая функция. Если вы не определили __init__ в классе, он было бы использует тот, что в метаклассе. Интересно отметить, что вы не можете переопределить подобные методы dunder на уровне экземпляра, потому что методы dunder всегда вызываются как type(self).__dunder__(self, ...) при использовании в качестве операторов.
Я думаю, что если я не определю __init__ в классе, он будет использовать его в своем суперклассе. Только если у суперкласса нет этого метода, он будет использовать метод из своего метакласса. Но object.__init__ существует всегда, поэтому тот, что находится в его метаклассе, использоваться не будет.
@ Сайкер прав. Object не имеет суперкласса, что упрощает этот случай.
Возможный дубликат Что может `__init__` сделать, чего не может `__new__`?