Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the type of the super object returned by super()?

From here:

super( [ type [ , object-or-type ]] )

Return a proxy object that delegates method calls to a parent or sibling class of type. This is useful for accessing inherited methods that have been overridden in a class. The search order is same as that used by getattr() except that the type itself is skipped.

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

  1. If I am correct, a type is a class, and a class is a type. A class is an object, so a type is also an object. Why does the quote distinguish the two cases when the second argument being an object when it is a type?

  2. When the second argument is a type, why is issubclass(type2, type) required to be true?

  3. What is the type of the super object returned by super in each of the three cases respectively? Or how do you determine the type of the super object returned by super?

    When the second argument is an object, because "The search order is same as that used by getattr() except that the type itself is skipped", I guessed that the type of the superobject returned by super function should be a subclass of any ancestry class of the first argument type, but I found that it is actually not by testing with issubclass. So did I misunderstand something?

like image 203
Tim Avatar asked Jul 08 '17 23:07

Tim


People also ask

What does super () return?

The super() function is used to give access to methods and properties of a parent or sibling class. The super() function returns an object that represents the parent class.

What is super () __ Init__ in Python?

Understanding Python super() with __init__() methodsThe super function returns a temporary object of the superclass that allows access to all of its methods to its child class. Note: For more information, refer to Inheritance in Python.

Is super () necessary Python?

In general it is necessary. And it's often necessary for it to be the first call in your init. It first calls the init function of the parent class ( dict ).

What is a superclass in Python?

The class from which a class inherits is called the parent or superclass. A class which inherits from a superclass is called a subclass, also called heir class or child class.


1 Answers

You seem to be confusing the word type with the type() built-in. Here they simply references the first argument passed into super().

What the documentation tells you is that if you pass in two arguments, then the second argument either has to be an instance of the first argument, or it has to be a subclass. In other words, either isinstance(first_argument, second_argument) or issubclass(first_argument, second_argument) must be true. There is no other meaning here.

Just like int() or str() or any of the other built-in types, the type of the object returned by calling super() is that type. There are no separate types returned for different arguments. See the C source code defining the object.

The super() object implements a __getattribute__ hook that implements specific attribute behaviour. The documentation tells you that the rules for attribute lookups are the same as for getattr() (but with the documented MRO skip), but that does not mean that super() returns an ancestor class.

What actually happens is that super().__getattribute__ takes the MRO of the second argument (either type(instance).__mro__ or cls.__mro__, depending on wether isinstance() or issubclass() was true), find the first argument in that sequence and start testing for attributes after that. Because the MRO is scanned for the (type of) the second argument first, it does have to be findable, which is why the constraints are what they are.

In Pure Python, this is what super() does (simplified to focus on just the two argument behaviour):

def _supercheck(type_, obj):
    try:
        if issubclass(obj, type_):
            return obj
    except TypeError:
        # obj is not a type so issubclass throws a TypeError
        pass
    if isinstance(obj, type_):
        return type(obj)
    raise TypeError(
        "super(type, obj): obj must be an instance or subtype of type")


class super_:
    def __init__(self, type_, obj):
        # simplified for the two-argument case
        self.type_ = type_
        self.obj = obj
        self.obj_type = _supercheck(type_, obj)

    def __getattribute__(self, name):
        if name == '__class__':
            # __class__ should always come from this object, not
            # the represented MRO.
            return super().__getattribute__(name)

        # avoid infinite recursion issues
        sd = super().__getattribute__('__dict__')
        starttype = sd['obj_type']
        type_ = sd['type_']
        obj = sd['obj']

        mro = iter(starttype.__mro__)

        # skip past the start type in the MRO
        for tp in mro:
            if tp == type_:
                break

        # Search for the attribute on the remainder of the MRO
        for tp in mro:
            attrs = vars(tp)
            if name in attrs:
                res = attrs[name]
                # if it is a descriptor object, bind it
                descr = getattr(type(res), '__get__', None)
                if descr is not None:
                    res = descr(
                        res,
                        None if obj is starttype else obj,
                        starttype)
                return res

        return super().__getattribute__(name)
like image 96
Martijn Pieters Avatar answered Sep 29 '22 03:09

Martijn Pieters