Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python super() - two argument version in context of __new__ [duplicate]

Tags:

python

I've read the documentation for super() multiple times but I still don't get what the two argument version returns.

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.

  1. What is a proxy object?
  2. parent or sibling?

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. That's all nice but what is returned? And what does the syntax super(a,b) mean?

In context of __new__,

Typical implementations create a new instance of the class by invoking the superclass’s __new__() method using super(currentclass, cls).__new__(cls[, ...]) with appropriate arguments and then modifying the newly-created instance as necessary before returning it.

super(currentclass, cls).__new__(cls[, ...])

If currentclass is equal to cls, you have super(currentclass, currentclass)

  1. What does that return? What does that syntax mean?
  2. How does this statement return an instance of cls so that __init__ will be called on the returned object?
like image 892
Bob Avatar asked Nov 08 '22 11:11

Bob


1 Answers

The answer to "what does super return" isn't anything like "it returns a list" or "it returns a modified copy of the second object". super returns an object of type super, a type designed specifically to have the qualities the documentation says it has.

Perhaps it would help to show a pure-Python implementation of super. If super wasn't written in C, it would look basically like this:

class super(object):
    def __init__(self, klass, object_or_klass):
        # The real super can be called with 0 arguments on Python 3,
        # but digging into the magic that makes that work isn't relevant here.

        if isinstance(object_or_klass, klass):
            mro = type(object_or_klass).__mro__
            self.obj_type = type(object_or_klass)
            self.obj = object_or_klass
        elif issubclass(object_or_klass, klass):
            mro = object_or_klass.__mro__
            self.obj_type = object_or_klass
            self.obj = None
        else:
            raise TypeError

        # Set up a copy of the MRO to search,
        # with everything up to and including klass skipped
        self.searchlist = mro[mro.index(klass)+1:]

    def __getattribute__(self, name):
        # self.searchlist would be infinite recursion, as would super().__getattribute__
        searchlist = object.__getattribute__(self, 'searchlist')

        # Search the method resolution order for the attribute we want.
        for klass in searchlist:
            if name in klass.__dict__:
                attr = klass.__dict__[name]
                break
        else:
            raise AttributeError

        if hasattr(attr, '__get__'):
            # Handle descriptors.
            obj = object.__getattribute__(self, 'obj')
            obj_type = object.__getattribute__(self, 'obj_type')
            attr = attr.__get__(obj, obj_type)
        return attr

Now you can see that super(a, b) constructs a super object, and super(a, b).whatever calls the __getattribute__ method of that super object to search the method resolution order of the second argument for the attribute we want. The attribute lookup procedure for b.whatever is very similar, just without chopping off the first part of the MRO, and checking the instance dict if b isn't a class.

like image 174
user2357112 supports Monica Avatar answered Nov 14 '22 23:11

user2357112 supports Monica