Subclassing tuple with multiple __init__ arguments

PythonInheritanceSubclassTuples

Python Problem Overview


The following code works:

class Foo(tuple):

    def __init__(self, b):
        super(Foo, self).__init__(tuple(b))

if __name__ == '__main__':
    print Foo([3, 4])

$ python play.py 

Result:

play.py:4: DeprecationWarning: object.__init__() takes no parameters
  super(Foo, self).__init__(tuple(b))
(3, 4)

But not the following:

class Foo(tuple):

    def __init__(self, a, b):
        super(Foo, self).__init__(tuple(b))

if __name__ == '__main__':
    print Foo(None, [3, 4])

$ python play.py 

Result:

Traceback (most recent call last):
  File "play.py", line 7, in <module>
    print Foo(None, [3, 4])
TypeError: tuple() takes at most 1 argument (2 given)

Why?

Python Solutions


Solution 1 - Python

Because tuples are immutable, you have to override __new__ instead:

python docs

> object.__new__(cls[, ...]) > > Called to create a new instance of > class cls. __new__() is a static > method (special-cased so you need not > declare it as such) that takes the > class of which an instance was > requested as its first argument. The > remaining arguments are those passed > to the object constructor expression > (the call to the class). The return > value of __new__() should be the new > object instance (usually an instance > of cls). > > Typical implementations create a new > instance of the class by invoking the > superclass’s __new__() method using > super(currentclass, cls).__new__(cls[, ...]) with appropriate arguments and > then modifying the newly-created > instance as necessary before returning > it. > > If __new__() returns an instance of > cls, then the new instance’s > __init__() method will be invoked like __init__(self[, ...]), where self is the new instance and the remaining > arguments are the same as were passed > to __new__(). > > If __new__() does not return an > instance of cls, then the new > instance’s __init__() method will not > be invoked. > > __new__() is intended mainly to allow subclasses of immutable types (like > int, str, or tuple) to customize > instance creation. It is also commonly > overridden in custom metaclasses in > order to customize class creation.

Solution 2 - Python

To assign the tuple value you need to override the __new__ method:

class Foo(tuple):

    def __new__ (cls, a, b):
        return super(Foo, cls).__new__(cls, tuple(b))

The arguments seem to be ignored by the __init__ implementation of the tuple class, but if you need to do some init stuff you can do it as follows:

class Foo(tuple):

    def __new__ (cls, a, b):
        return super(Foo, cls).__new__(cls, tuple(b))

    def __init__(self, a, b):
        self.a=a
        self.b=b
    
if __name__ == '__main__':
    foo = Foo(None, [3, 4])
    print foo
    print foo.a
    print foo.b

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSridhar RatnakumarView Question on Stackoverflow
Solution 1 - PythonJohn La RooyView Answer on Stackoverflow
Solution 2 - PythonJonatan AnauatiView Answer on Stackoverflow