2

Please see the following code:

#! /usr/bin/env python3

class MyTupleT(tuple):
    def __init__(self, contents):
        tuple.__init__(contents)
        self.len = len(contents)

class MyTupleS(tuple):
    def __init__(self, contents):
        super().__init__(contents)
        self.len = len(contents)

t = MyTupleT([0, 1, 2])
print(t.len, t)

s = MyTupleS([0, 1, 2])
print(s.len, s)

The output is:

3 (0, 1, 2)
Traceback (most recent call last):
File "tuple_init.py", line 16, in <module>
    s = MyTupleS([0, 1, 2])
File "tuple_init.py", line 10, in __init__
    super().__init__(contents)
TypeError: object.__init__() takes no parameters

The docs say that super() will:

Return a proxy object that delegates method calls to a parent or sibling class of type

IIUC here super() is the same as super(MyTupleS, self). Now the docs further say that

The search order is same as that used by getattr() except that the type itself is skipped. The __mro__ attribute of the type lists the method resolution search order used by both getattr() and super().

Ohkay but then:

$ MyTupleS.__mro__
(__main__.MyTupleS, tuple, object)

So the super() line should first look within tuple to find __init__ and tuple's __init__ can certainly take one parameter (other than self of course) and is what is being called by the explicit tuple.__init__ line. So why doesn't the super().__init__ call work identical to that? Why is it trying to invoke object.__init__ and failing?

IIUC one should only need to explicitly specify the superclass when one is worried that the inheritance hierarchy can change in a "dynamic execution environment" (phrase from the docs) and wants the method of a particular superclass to be called. But this error effectively makes it mandatory for the superclass to be specified. Why?

NOTE: This is different from another similarly titled question.

jamadagni
  • 1,214
  • 2
  • 13
  • 18
  • Not sure what's wrong with the question to get a downvote? – jamadagni Dec 04 '17 at 05:58
  • https://stackoverflow.com/questions/10165405/#comment13041331_10165405 says `super()` and `__init__` don't go well together which is surprising because IIUC often one would want to call `super()` from `__init__`! – jamadagni Dec 04 '17 at 05:59
  • 1
    The first version only _appears_ to work because you've called `tuple.__init__()` without passing `self` (well, actually, you've passed the list `[0, 1, 2]` as `self`). –  Dec 04 '17 at 06:05
  • 1
    Oh yes when instance methods are called by a class name, we should manually pass `self`. Thanks for reminding that! – jamadagni Dec 04 '17 at 06:09
  • Downvoting legitimate questions which are receiving legitimate answers discourages non-experts like me from asking on SO for fear of downvote itself! – jamadagni Dec 04 '17 at 06:11
  • Donno whether the downvoter recanted or someone else upvoted. Thanks either way! – jamadagni Dec 04 '17 at 06:18

1 Answers1

3

If you call tuple.__init__ it returns object.__init__ because tuple has no custom __init__ method and only inherits it from object. The first argument for object.__init__ is self and what object.__init__ does is nothing. So when you pass in contents it's interpreted as self and doesn't throw an Exception. However it probably doesn't do what you think it does because tuple.__new__ is responsible for setting up a new tuple instance.

If you use super().__init__ it also resolves to object.__init__ but it already binds the current "self" as first argument. So when you pass contents to this function it's interpreted as additional argument which doesn't exist for object.__init__ and therefore throws that Error.

MSeifert
  • 145,886
  • 38
  • 333
  • 352
  • Hi thanks for this but why should `tuple.__init__` resolve to `object.__init__` when tuple has its own `__init__`? – jamadagni Dec 04 '17 at 06:09
  • 1
    @jamadagni It doesn't have a "custom" `__init__` it just inherits it from `object`. That was what I meant. :) – MSeifert Dec 04 '17 at 06:10
  • Then something is wrong with PyCharm 2017.1.1 showing me `def __init__(self, seq=()): # known special case of tuple.__init__` under `class tuple(object):` of `builtins.py`. Is that only an artificial Py file and really it's all inside the C implementation? – jamadagni Dec 04 '17 at 06:14
  • 1
    @jamadagni I only looked at the [source code for tuple and that doesn't define an `__init__`](https://github.com/python/cpython/blob/master/Objects/tupleobject.c#L840). That means it inherits it from `object`. But it does have a `__new__` method (a few lines later, it's called `tp_new`). – MSeifert Dec 04 '17 at 06:17
  • but then how does the `MyTupleT` store `contents` if the call to `tuple.__init__` isn't doing the lifting? Is it because `tuple.__new__` is implicitly called with `contents`? – jamadagni Dec 04 '17 at 06:19