Is there any reason to choose __new__ over __init__ when defining a metaclass?
Question:
I’ve always set up metaclasses something like this:
class SomeMetaClass(type):
def __new__(cls, name, bases, dict):
#do stuff here
But I just came across a metaclass that was defined like this:
class SomeMetaClass(type):
def __init__(self, name, bases, dict):
#do stuff here
Is there any reason to prefer one over the other?
Update: Bear in mind that I’m asking about using __new__
and __init__
in a metaclass. I already understand the difference between them in another class. But in a metaclass, I can’t use __new__
to implement caching because __new__
is only called upon class creation in a metaclass.
Answers:
You can implement caching. Person("Jack")
always returns a new object in the second example while you can lookup an existing instance in the first example with __new__
(or not return anything if you want).
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__
.
As has been said, if you intend to alter something like the base classes or the attributes, you’ll have to do it in __new__
. The same is true for the name
of the class but there seems to be a peculiarity with it. When you change name
, it is not propagated to __init__
, even though, for example attr
is.
So you’ll have:
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__
prints
I am still called 'A' in init
Now I'm A_class_named_A
This is important to know, if __init__
calls a super metaclass which does some additional magic. In that case, one has to change the name again before calling super.__init__
.
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
I’ve always set up metaclasses something like this:
class SomeMetaClass(type):
def __new__(cls, name, bases, dict):
#do stuff here
But I just came across a metaclass that was defined like this:
class SomeMetaClass(type):
def __init__(self, name, bases, dict):
#do stuff here
Is there any reason to prefer one over the other?
Update: Bear in mind that I’m asking about using __new__
and __init__
in a metaclass. I already understand the difference between them in another class. But in a metaclass, I can’t use __new__
to implement caching because __new__
is only called upon class creation in a metaclass.
You can implement caching. Person("Jack")
always returns a new object in the second example while you can lookup an existing instance in the first example with __new__
(or not return anything if you want).
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__
.
As has been said, if you intend to alter something like the base classes or the attributes, you’ll have to do it in __new__
. The same is true for the name
of the class but there seems to be a peculiarity with it. When you change name
, it is not propagated to __init__
, even though, for example attr
is.
So you’ll have:
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__
prints
I am still called 'A' in init
Now I'm A_class_named_A
This is important to know, if __init__
calls a super metaclass which does some additional magic. In that case, one has to change the name again before calling super.__init__
.
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 alterbases
(which can cause MRO resolution errors if you’re not careful). I’ll call that variablemcls
, to differentiate it from the usualcls
referring to application level class. -
__init__
gets the newly-created application-level class,Bar
andFoo
and, by that time, this class’s namespace has been populated, seecls_attrib
in example below. I’ll stick tocls
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