How to interpret the error message "Foo() takes no arguments" when specifying a class instance as base class?

72 Views Asked by At

The following code:

>>> class Foo: pass
>>> class Spam(Foo()): pass

will of course raise an error message:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Foo() takes no arguments

But the error info is a little bit odd. Seems that there are some processes when initializing class Spam.

I wonder which step causes the error. In other words, why can't class inherit instance, the error message seems to indicate it indeed try something.


Note: I know there will be no error if write like this class Spam(Foo). But I do it on purpose. I just can't understand the error message, which indicates some procedures exist when class inheriting, I want to know which one procedure causes it.

1

There are 1 best solutions below

0
MegaIng On BEST ANSWER

Using a few dirty print statements we can get the sequence of events that is happening:

class Foo:
    def __init__(self, *args, **kwargs):
        print("__init__", self, args, kwargs)

    def __new__(cls, *args, **kwargs):
        print("__new__", cls, args, kwargs)
        return super().__new__(cls)

inst = Foo()

class Spam(inst): pass

print(inst, Spam)

outputs

__new__ <class '__main__.Foo'> () {}
__init__ <__main__.Foo object at 0x0000019FFB399550> () {}
__new__ <class '__main__.Foo'> ('Spam', (<__main__.Foo object at 0x0000019FFB399550>,), {'__module__': '__main__', '__qualname__': 'Spam'}) {}
__init__ <__main__.Foo object at 0x0000019FFB399590> ('Spam', (<__main__.Foo object at 0x0000019FFB399550>,), {'__module__': '__main__', '__qualname__': 'Spam'}) {}
<__main__.Foo object at 0x0000019FFB399550> <__main__.Foo object at 0x0000019FFB399590>

So trying to inherit from an instance makes Python create a new instance of that class giving it parameters that would normally go to type.__new__.

The reasons this happens is because types in python are objects: When deriving a class, python tries to get the correct meta class by calling type(base), which in this situation returns Foo (the class), and then it tries to create an instance of that. ( I could have sworn python checks that type(base) is a subclass of type, but apparently that is wrong)