Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Difficulty extending tuples in Python

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.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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)

>Solution :

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.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading