Several differences, in fact.
For one thing, the first argument in __new__
and __init__
are not the same, which is not made clear by everyone insisting on just using cls
in both cases (despite the fact that the variable name doesn't hold any particular meaning). 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). I'll call that variable mcls
, to differentiate it from the usual cls
referring to application level class.
__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. I'll stick to cls
as per usual naming convention.
Sample code:
class Mixin:
pass
class 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):
pass
print("\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