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

Есть ли причина выбирать __new__ над __init__ при определении метакласса?

Я всегда настраивал метаклассы примерно так:

class SomeMetaClass(type):
    def __new__(cls, name, bases, dict):
        #do stuff here

Но я просто столкнулся с метаклассом, который был определен следующим образом:

class SomeMetaClass(type):
    def __init__(self, name, bases, dict):
        #do stuff here

Есть ли какая-то причина предпочесть друг другу?

Обновить. Имейте в виду, что я прошу об использовании __new__ и __init__ в метаклассе. Я уже понимаю разницу между ними в другом классе. Но в метаклассе я не могу использовать __new__ для реализации кэширования, потому что __new__ вызван только созданием класса в метаклассе.

4b9b3361

Ответ 1

Если вы хотите изменить атрибуты dict перед созданием класса или изменить базовый кортеж, вы должны использовать __new__. К моменту появления __init__ аргументов объект класса уже существует. Кроме того, вы должны использовать __new__, если хотите вернуть что-то другое, кроме вновь созданного класса соответствующего типа.

С другой стороны, к моменту выполнения __init__ класс существует. Таким образом, вы можете делать что-то вроде ссылки на только что созданный класс на один из его объектов-членов.

Изменить: изменилась формулировка, чтобы сделать более понятным, что под "объектом" я подразумеваю класс-объект.

Ответ 2

Вы можете увидеть полную запись в официальные документы, но в основном, __new__ вызывается до создания нового объекта (для цель его создания) и __init__ вызывается после создания нового объекта (с целью его инициализации).

Использование __new__ позволяет использовать трюки, такие как кэширование объектов (всегда возвращающее один и тот же объект для одних и тех же аргументов, а не создание новых) или создание объектов другого класса, чем запрошено (иногда используется для возврата более конкретных подклассов запрошенного класса). Как правило, если вы не делаете что-то довольно странное, __new__ имеет ограниченную полезность. Если вам не нужно ссылаться на такие обманки, придерживайтесь __init__.

Ответ 3

Вы можете реализовать кэширование. Person("Jack") всегда возвращает новый объект во втором примере, в то время как вы можете искать существующий экземпляр в первом примере с помощью __new__ (или не возвращать что-либо, если хотите).

Ответ 4

Как уже было сказано, если вы собираетесь изменить что-то вроде базовых классов или атрибутов, вы должны сделать это в __new__. То же самое верно для name класса, но, похоже, с ним есть особенность. Когда вы меняете name, он не распространяется на __init__, хотя, например, attr есть.

Итак, у вас есть:

class Meta(type):
    def __new__(cls, name, bases, attr):
        name = "A_class_named_" + name
        return type.__new__(cls, name, bases, attr)

    def __init__(cls, name, bases, attr):
        print "I am still called '" + name + "' in init"
        return super(Meta, cls).__init__(name, bases, attr)

class A(object):
    __metaclass__ = Meta

print "Now I'm", A.__name__

печатает

I am still called 'A' in init
Now I'm A_class_named_A

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