Subclassing tuple with multiple __init__ arguments
PythonInheritanceSubclassTuplesPython 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:
> 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