开发者

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

开发者 https://www.devze.com 2022-12-13 00:04 出处:网络
I\'ve always set up metaclasses something like this: class SomeMetaClass(type): def __new__(cls, name, bases, dict):

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 a开发者_开发技巧bout 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.


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 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:
        #                                   
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号