Search code examples
pythonpython-3.7supermetaclass

super().__new__() for object vs type in Python 3.7


Calling super().__new__() when overriding __new__ in a metaclass contains the following function signature:

class MyMeta(type):
    
    def __new__(cls, name, bases, classdict):
        clsobj = super().__new__(cls, name, bases, classdict)

However, when overriding __new__ in a normal class, we have the following:

class A:

    def __new__(cls, *a, **kw):
        clsobj = super().__new__(cls)

Passing any other arguments to super() in A.__new__ will lead to the following error:

TypeError: object.__new__() takes exactly one argument (the type to instantiate)

I understand that in the second case, we are dealing with object.__new__ while in the first case we are dealing with type.__new__.

My question is, why are these function signatures different? Why does object.__new__ only accept cls?


Solution

  • object and type have an interesting relationship. object is an instance of type, but type is a subclass of object.

    __new__, however, focuses on the creation of an instance of a class, and so object.__new__ is the lowest common denominator: it does virtually nothing on its own, because there is virtually nothing that instances of every possible class have in common. As such, object.__new__ needs no more information than the type its return value will have.

    type.__new__ does quite a bit more than object.__new__: it has to create an object that itself is capable of creating new objects. This requires quite a bit more information, so type.__new__ defines several additional parameters when it overrides object.__new__.


    Note, however, that type itself does not use super; if you define two classes

    class A:
        def __new__(cls, *args, **kwargs):
            print("In A.__new__")
            return super().__new__(cls)
    
    
    class B(type, A):
        def __new__(cls, name, bases, dct, *args, **kwargs):
            print("In B.__new__")
            return super().__new__(cls, name, bases, dct)
    

    you'll see that B("B", (), {}) outputs In B.__new__ but not In A.__new__. type.__new__ is the end of the line, so to speak, for creating metaclasses.

    Typically, though, you wouldn't mix classes like this. Just like you rarely include object in the list of base classes for a class (I'm almost willing to say you never would), you don't often inherit from type and another (meta)class, and I can't think of any reason why you would try to inherit from both type and a non-type subclass of object.