Is there any reason to choose __new__ over __init__ when defining a metaclass? Is there any reason to choose __new__ over __init__ when defining a metaclass? python python

Is there any reason to choose __new__ over __init__ when defining a metaclass?


If you want to alter the attributes dict before the class is created, or change the bases tuple, you have to use __new__. By the time __init__ sees the arguments, the class object already exists. Also, you have to use __new__ if you want to return something other than a newly created class of the type in question.

On the other hand, by the time __init__ runs, the class does exist. Thus, you can do things like give a reference to the just-created class to one of its member objects.

Edit: changed wording to make it more clear that by "object", I mean class-object.


You can see the full writeup in the official docs, but basically, __new__ is called before the new object is created (for the purpose of creating it) and __init__ is called after the new object is created (for the purpose of initializing it).

Using __new__ allows tricks like object caching (always returning the same object for the same arguments rather than creating new ones) or producing objects of a different class than requested (sometimes used to return more-specific subclasses of the requested class). Generally, unless you're doing something pretty odd, __new__ is of limited utility. If you don't need to invoke such trickery, stick with __init__.


Several differences, in fact.

For one thing, the first argument in __new__ and __init__ are not the same, which isn't helped by everyone just using, cls. Someone pointed this out and it's core to understanding the difference:

  • __new__ gets the metaclass - MyType in my example (remember the application-level class is not created yet). This is where you can alter bases (which can cause MRO resolution errors if you're not careful).

  • __init__ gets the newly-created application-level class, Bar and Foo and, by that time, this class's namespace has been populated, see cls_attrib in example below.

Sample code:

class Mixin:    passclass MyType(type):    def __new__(mcls, name, bases, attrs, **kwargs):        print("  MyType.__new__.mcls:%s" % (mcls))        if not Mixin in bases:            #could cause MRO resolution issues, but if you want to alter the bases            #do it here            bases += (Mixin,)        #The call to super.__new__ can also modify behavior:        #                                   👇 classes Foo and Bar are instances of MyType        return super(MyType, mcls).__new__(mcls, name, bases, attrs)        #now we're back to the standard `type`         #doing this will neuter most of the metaclass behavior, __init__ wont        #be called.                         👇        #return super(MyType, mcls).__new__(type, name, bases, attrs)    def __init__(cls, name, bases, attrs):        print("  MyType.__init__.cls:%s." % (cls))        #I can see attributes on Foo and Bar's namespaces        print("    %s.cls_attrib:%s" % (cls.__name__, getattr(cls, "cls_attrib", None)))        return super().__init__(name, bases, attrs)print("\n Foo class creation:")class Foo(metaclass=MyType):    passprint("\n bar class creation:")class Bar(Foo):    #MyType.__init__ will see this on Bar's namespace    cls_attrib = "some class attribute"

output:

 Foo class creation:  MyType.__new__.mcls:<class '__main__.test.<locals>.MyType'>  MyType.__init__.cls:<class '__main__.test.<locals>.Foo'>.    Foo.cls_attrib:None Bar class creation:  MyType.__new__.mcls:<class '__main__.test.<locals>.MyType'>  MyType.__init__.cls:<class '__main__.test.<locals>.Bar'>.    Bar.cls_attrib:some class attribute