Subclassing tuple with multiple __init__ arguments

Question:

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?

Asked By: Sridhar Ratnakumar

||

Answers:

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.

Answered By: John La Rooy

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
Answered By: Jonatan Anauati