I'm using Python 3.9.6. Why do the classes A
and B
work with the *args
as a parameter, but C
does not? Python throws this error (before printing args
):
TypeError: tuple expected at most 1 argument, got 3
.
Note the only difference between the classes is if it is extending a class, and what class it's extending. I'm asking because I'm trying to make an object that behaves exactly like a tuple but would be distinguishable from it using isinstance
.
class A:
def __init__(self, *args):
print(args)
class B(list):
def __init__(self, *args):
print(args)
super().__init__(args)
class C(tuple):
def __init__(self, *args):
print(args)
super().__init__(args)
A(1,2,3)
B(4,5,6)
C(7,8,9)
CodePudding user response:
Note that in your example class C
, the value (7, 8, 9)
is not printed before the error message is shown. That means your __init__
method is not being called at all.
In fact, the error is raised by the __new__
method, not the __init__
method. It looks like the difference between B
and C
is caused by the fact that list.__new__
accepts any number of arguments (and ignores them), whereas tuple.__new__
does not:
>>> list.__new__(B, 4, 5, 6)
[]
>>> tuple.__new__(C, 7, 8, 9)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: tuple expected at most 1 argument, got 3
Note that list.__new__
just creates the list object, it doesn't populate it (list.__init__
does that). I can't say for certain why one accepts extra arguments while the other doesn't, but that's the reason for this discrepancy, anyway.
To fix it, your class C
needs to override __new__
in order to pass the correct number of arguments to tuple.__new__
:
class C(tuple):
def __new__(cls, *args):
print(args)
return super().__new__(cls, args)
In this case there is no need to also override __init__
, though you still can if you want to; however, note that tuple
doesn't override __init__
, so your super().__init__()
will actually invoke object.__init__
, which accepts no arguments and does nothing, so you must invoke it with no arguments.