В чем разница между методами __new__ и __init__ в Python
Что проверяют
Понимаете ли вы, как Python создаёт объект: кто выделяет экземпляр, кто его настраивает и в каком порядке это происходит. Следом обычно просят показать, где __new__ действительно нужен, и спрашивают про наследование от int или tuple.
Как отвечать
Вызов MyClass(...) на деле обрабатывает type.__call__, и он делает два шага:
__new__(cls, ...): получает класс и возвращает новый экземпляр, обычно черезsuper().__new__(cls). Это статический метод, но объявлять@staticmethodне нужно, Python делает это сам;__init__(self, ...): получает уже созданный объект и заполняет атрибуты. Должен вернутьNone, иначе будетTypeError;- связь:
__init__вызывается, только если__new__вернул экземплярclsили его подкласса. Вернули объект другого типа, и__init__пропускается; - аргументы: оба метода получают те же аргументы, что переданы в вызов класса.
Где __new__ нужен на практике:
- неизменяемые типы: наследуясь от
str,int,tuple,frozenset, значение можно задать только в__new__, к моменту__init__объект уже не изменить; - контроль создания: кэш экземпляров, синглтон, возврат подкласса в зависимости от аргументов;
- метаклассы: там
__new__создаёт сам класс, и переопределяют его чаще всего именно в метаклассах.
В обычном прикладном коде хватает __init__, а синглтон проще сделать объектом на уровне модуля.
Где спотыкаются
Называют __init__ конструктором в духе Java. К моменту его вызова объект уже существует, это инициализатор, и отличие как раз в этом.
Пишут синглтон через __new__ и забывают, что __init__ всё равно отработает при каждом вызове класса и перезапишет состояние единственного экземпляра.
Передают аргументы дальше: super().__new__(cls, *args). Если класс переопределил __new__, object.__new__ на лишние аргументы отвечает TypeError, туда передают только cls.