1

I am wondering why the syntax for calling the super classes' constructors has the Child object passed as an argument before the self, and what purpose those arguments serve as to the goal of inheriting the methods of the parent class.

class Child(SomeBaseClass):
    def __init__(self):
        super(Child, self).__init__()

I've read the following posts, but in either have I found the reasoning for this:

  1. The 'super' docs

  2. What does 'super' do in Python?

  3. In this post I've found this quote:

    super(class, subclass).method returned a bound method, not an unbound one

    but still could not get my head around the meaning of this syntax.

Appreciate an elaboration of this syntax.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Michael
  • 2,167
  • 5
  • 23
  • 38
  • 6
    Just to be clear: You are asking why the Python language developers decided to use ``super(cls, self)`` instead of ``super(self, cls)``? – MisterMiyagi Sep 21 '20 at 10:11
  • 3
    I don't know what you mean by *"why"* (that's the defined order of arguments selected by the Python maintainers: https://docs.python.org/3/library/functions.html#super), and from a syntax perspective that's just standard function calling either way around. – jonrsharpe Sep 21 '20 at 10:12
  • 3
    The simple answer is *this is what the designers chose*... A more reasonable one is that logically it reads as *the `super` method of class `Child` for object `self`*. Lastly, that's the old way of calling `super` and today you should just call `super()` so your problem is solved – Tomerikoo Sep 21 '20 at 10:12
  • 2
    It is a good question. Usually self comes before a dot, but it is an implicit FIRST argument.So, where self cannot come before a dot, why is it not FIRST? – Joshua Fox Sep 21 '20 at 10:14
  • @MisterMiyagi, yes. It is unclear to me. Is it only a convention or is there some sort of meaning behind it? – Michael Sep 21 '20 at 10:14
  • 4
    `self` is only the implicit first argument when you're calling a *method* (bound to the specific instance that's automagically passed as `self`); `super` is a *function*. – jonrsharpe Sep 21 '20 at 10:15
  • @Tomerikoo thanks for your simple explanation - it really answers my question. – Michael Sep 21 '20 at 10:16
  • 2
    I was just about to write what jon just said. `super` is a built-in **function**. Hence this order might even be on purpose to distinguish it from a standard instance method and not have people trying `self.super(cls)` – Tomerikoo Sep 21 '20 at 10:17
  • @jonrsharpe - great explanation! Thanks! – Michael Sep 21 '20 at 10:19
  • 1
    Note that [the initial idea of ``super``](https://mail.python.org/archives/list/python-dev@python.org/thread/UBHMOZQ6SVBHUOM3KHUMPH3CB7L3GLAI/#5SJ3P7TMPDKF7COB5QF4VQHFCPE55LIU) seems to have been ``super(self, MyBaseClass).foo(arg, ...)``. – MisterMiyagi Sep 21 '20 at 10:43

1 Answers1

1

The order of super arguments reflects Python's idea of unbound and bound methods/descriptors. In short, the second argument is optional and thus must come after the required first argument.

Built-in Functions: super([type[, object-or-type]])

[...]

If the second argument is omitted, the super object returned is unbound. If the second argument is an object, isinstance(obj, type) must be true. If the second argument is a type, issubclass(type2, type) must be true (this is useful for classmethods).

This reflects how a method call self.method() is equivalent to Class.method(self), i.e. the order of operands is Class then self.*


Python methods/descriptors come in two flavours: unbound on their defining class, and bound on their instance.*

>>> class Base:
...     def method(self): print('called Base method')
...
>>> Base.method     # unbound method
<function __main__.Base.method(self)>
>>> Base().method   # bound method
<bound method Base.method of <__main__.Base object at 0x10dd0e910>>
>>> Base().method()
called Base method

A bound descriptor is created by taking an unbound descriptor and binding it to an instance. This is encoded and implemented in the descriptor protocol.

>>> instance = Base()
>>> unbound = Base.method
>>> unbound.__get__(instance)
<bound method Base.method of <__main__.Base object at 0x10dd14510>>
>>> unbound.__get__(instance)()
called Base method

The super type is by default unbound. Binding it via the descriptor protocol or by passing an instance is equivalent.

>>> class Child(Base): ...
>>> instance = Child()
>>> super(Child, instance)
<super: __main__.Child, <__main__.Child at 0x10dcda9d0>>
>>> super(Child).__get__(instance)
<super: __main__.Child, <__main__.Child at 0x10dcda9d0>>

In either case, the class must be passed first before the instance.


From the python-dev archive:

Add 'super', another new object type with magical properties.

super(type) -> unbound super object

super(type, obj) -> bound super object; requires isinstance(obj, type)

Typical use to call a cooperative superclass method:

class C(B):
    def meth(self, arg):
        super(C, self).meth(arg);

* This description glosses over the finer details of the descriptor protocol. For example, a method/descriptor can be bound to a class as well.

MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119