The following toy example is a bit strange-looking, but it shows the problem as simply as I can put it.
First, the unproblematic part:
class TupleWrapper(tuple):
def __new__(cls, ignored):
return super(TupleWrapper, cls).__new__(cls, [1, 2, 3])
The class TupleWrapper
takes an arbitrary an returns a tuple-like object analogous to the constant (1, 2, 3)
. For example:
>>> TupleWrapper('foo')
(1, 2, 3)
>>> TupleWrapper(8)
(1, 2, 3)
So far so good.
Now, consider this class:
class ListWrapper(list):
def __new__(cls, ignored):
return super(ListWrapper, cls).__new__(cls, [1, 2, 3])
It is identical to TupleWrapper
except that it subclasses list
instead of tuple
. Therefore, I expected the following
>>> ListWrapper('foo')
[1, 2, 3]
>>> ListWrapper(8)
[1, 2, 3]
In fact, what I get is
>>> ListWrapper('foo')
['f', 'o', 'o']
>>> ListWrapper(8)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
FWIW, I'm using Python 2.7.13.
Can someone help me understand this difference in behavior?
Is it possible to make sense of it, or is it "just one of those quirks" one just has to live with?
The difference is that tuples are immutable, so all work is done in __new__
. Lists are mutable, so their construction happens in __init__
. You haven't overridden the list.__init__
method, so it is still constructing the list as usual.
PS: inheriting from built-ins like list or tuple is usually a frustrating and disappointing experience anyway, so don't bother :)