Jeremy has also mentioned this issue briefly here: Fastai v2 chat - #183 by jeremy
AFAIK, because if __new__()
is defined, it always goes before __init__()
, and when creating a new subclass, the signature still belongs to its superclass if subclass didn’t define its own __new__()
.
class A():
def __new__(cls, *args, **kwargs): return super().__new__(cls, *args, **kwargs)
class B(A):
def __init__(self, a): self.a = a
B(a=1)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<some_obj_hash> in <module>
----> 1 B(a=1)
<another_obj_hash> in __new__(cls, *args, **kwargs)
1 class A():
----> 2 def __new__(cls, *args, **kwargs): return super().__new__(cls, *args, **kwargs)
TypeError: object.__new__() takes exactly one argument (the type to instantiate)
The reasoning behind it is often pointed to Liskov substitution principle, despite Python is keen to duck-typing. Plus, this way seems more convenient for Python core developers when dealing with MRO (Method Resolution Order) and/or underlying C functions.
To imitate Jeremy’s solution with a non-meta superclass like A
here, one way is to change subclass B
verbosely:
class B(A):
def __new__(cls, a, *args, **kwargs):
res = super().__new__(cls, *args, **kwargs)
res.__signature__ = inspect.signature(res.__init__)
return res
def __init__(self, a): self.a = a
Not only it’s annoying and error-prone to change subclasses, but B
’s signature will have three arguments a, *args, **kwars
rather than just one a
. So probably another reason to have a meta superclass and change the signature in it.