Почему __new__ в классах нового стиля Python не является методом класса? - PullRequest
34 голосов
/ 01 февраля 2012

Список изменений для Python 2.2 (где были введены классы нового стиля) говорит следующее о функции __new__:

__new__ - это статический метод, а не метод класса. Сначала я подумал, что это должен быть метод класса, и поэтому я добавил примитив classmethod. К сожалению, с методами класса upcalls не работают правильно в этом случае, поэтому мне пришлось сделать его статическим методом с явным классом в качестве первого аргумента.

Однако я не могу думать о том, почему методы класса не будут работать для этой цели, и это, безусловно, будет выглядеть лучше. Почему __new__ в конце концов не превратился в метод класса? На что ссылается Гвидо, когда говорит, что «вызовы не работают правильно в этом случае»?

1 Ответ

22 голосов
/ 01 февраля 2012

__new__ статический метод позволяет использовать сценарий использования при создании в нем экземпляра подкласса:

return super(<currentclass>, cls).__new__(subcls, *args, **kwargs)

Если new является методом класса, то приведенное выше записывается как:

return super(<currentclass>, cls).new(*args, **kwargs)

и негде поставить subcls.

Я действительно не вижу, когда это будет правильным использованием __new__, хотя. Может быть, я этого не вижу, но мне кажется, что это совершенно патологическое использование (и следует сказать, что если вы все еще действительно этого хотите, то вы можете получить к нему доступ с помощью object.__new__.__func__). По крайней мере, мне трудно представить, что Гвидо мог бы изменить __new__ с метода класса на статический метод.

Более распространенным случаем будет вызов родителя __new__ без использования super(). Вам нужно место для явной передачи cls, в этом случае :

class Base(object):
    @classmethod
    def new(cls):
        print("Base.new(%r)" % (cls,))
        return cls()

class UseSuper(Base):
    @classmethod
    def new(cls):
        print("UseSuper.new(%r)" % (cls,))
        return super(UseSuper, cls).new() # passes cls as the first arg

class NoSuper(Base):
    @classmethod
    def new(cls):
        print("NoSuper.new(%r)" % (cls,))
        return Base.new()  # passes Base as the first arg

class UseFunc(Base):
    @classmethod
    def new(cls):
        print("UseFunc.new(%r)" % (cls,))
        return Base.new.im_func(cls)  # or `.__func__(cls)`. # passes cls as the first arg

print(UseSuper.new())
print('-'*60)
print(NoSuper.new())
print('-'*60)
print(UseFunc.new())
...