Vladimir Zolotykh
Vladimir Zolotykh

Reputation: 529

super() printable representation

sup = super(B, self) and sup2 = super(B, B) have indistinguishable representations, they both look like this:

<super: <class 'B'>, <B object>>

while super(B, self).spam gives a bound method, super(B, B) only works with class methods, so super(B, B).cls_spam(), like demonstrated below. You can't use it for regular methods, you get a normal function:

class A:
    @classmethod
    def cls_spam(cls):
        print('A.cls_spam: cls =', cls)

    def spam(self):
        print('A.spam: self =', self)

class B(A):
    def call_spam(self):
        sup = super(B, self)
        print('B.call_spam: sup =', sup)
        print('B.call_spam: sup.spam =', sup.spam)
        print('B.call_spam: sup.cls_spam =', sup.cls_spam)
        sup.spam()
        sup.cls_spam()

        sup2 = super(B, B)
        print('B.call_spam: sup2 =', sup2)
        print('B.call_spam: sup2.css_spam =', sup2.cls_spam)
        # can't call sup2.spam(), not without giving it self explicitly
        sup2.cls_spam()

The following interactive session illustrates:

>>> b = B()
>>> b.call_spam3()
B.call_spam: sup = <super: <class 'B'>, <B object>>
B.call_spam: sup.spam = <bound method A.spam of <__main__.B object at 0x108830b50>>
B.call_spam: sup.cls_spam = <bound method A.cls_spam of <class '__main__.B'>>
A.spam: self = <__main__.B object at 0x108830b50>
A.cls_spam: cls = <class '__main__.B'>
B.call_spam: sup2 = <super: <class 'B'>, <B object>>
B.call_spam: sup2.css_spam = <bound method A.cls_spam of <class '__main__.B'>>
A.cls_spam: cls = <class '__main__.B'>

super() is a complicated subject, if the demonstrated above behavior is documented understanding it would help me a lot.

Using Python 3.5.3,Debian GNU/Linux 9.11 (stretch)

Upvotes: 0

Views: 98

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1123710

super() is meant to be useful both in class methods and in regular methods. In a classmethod there is no instance, you only have access to the class, so the second argument to super() accepts either an instance or a class. That at least is covered in the documentation for super():

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).

Bold emphasis mine.

Now, the primary task super() has to perform is to search for attributes along the Method Resolution Order (MRO) list of classes, given a starting point. The starting point is the first argument, but the MRO has to be taken from the second argument; if you use super() in class Foo, you can't know at that time if Foo might have been subclassed, which can alter the MRO of the second argument. So, for this purpose, super() tracks two pieces of information:

  1. The class you want to use as a starting point for the search (the first argument)
  2. The class from which the MRO is taken (the type of the second argument if it is an instance, or just the second argument, if it is a class).

There is also a 3rd piece of information, the instance or class to which attributes are bound, when you do an attribute lookup. That's simply the second argument itself, so either an instance or class. The repr() output reflects just the first two values, in large part because those are 'hidden' in that super(), without arguments, gets its arguments from the context and so you can't, as easily see what the starting point is, or what the MRO source is, but you can much more easily see the first argument to the method (so self or cls).

If you want to distinguish between your two super() instances, you can instead look at the __self__ attribute, which represents that 3rd piece of information:

>>> sup = super(B, b)
>>> sup.__self__
<__main__.B object at 0x108b4c520>
>>> sup2 = super(B, B)
>>> sup2.__self__
<class '__main__.B'>

The other two pieces of information you do see in the repr() output are the __thisclass__ and __self_class__ attributes, respectively:

>>> sup.__thisclass__, sup.__self_class__
(<class '__main__.B'>, <class '__main__.B'>)

This is easier to spot when you use a different class as the first argument:

>>> sup_a = super(A, b)
>>> sup_a
<super: <class 'A'>, <B object>>
>>> sup_a.__thisclass__, sup_a.__self_class__
(<class '__main__.A'>, <class '__main__.B'>)

Upvotes: 4

Related Questions