Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is meant by super() returning a proxy object?

Tags:

python

super

According to the documentation,

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

The closest answer to what I need is found 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.

My interpretation is, super() returns an object of the specified type. However, I'm not sure why the term 'proxy' is used, does it have to the with the design pattern named 'proxy'? For example,

class Contact:
    def __init__(self, phone, email):
        self.phone = phone
        self.email = email

class Friend(Contact):
    def __init__(self, name, phone, email):
        self.name = name
        super().__init__(phone, email)

For this case, as far as I'm concerned, super().__init__(phone, email) is equivalent to using Contact.__init__(self, phone, email). That is, an instance of Contact is returned from super() for which we define a new attribute name, but I don't see how it would relate to that design pattern.

like image 227
Nameless Avatar asked Sep 05 '25 03:09

Nameless


2 Answers

That is, an instance of Contact is returned from super() for which we define a new attribute name

No, it doesn't. You can check by looking at print(type(super())). super() returns an instance of super... super is actually a class! This objects acts as a proxy, i.e. it proxies the attribute lookup to the next class in the method resolution order. Here is how you might implement it in pure Python:

class SimpleSuper:
    def __init__(self, cls, instance):
        self.cls = cls
        self.instance = instance
    def __getattr__(self, name):
        mro = type(self.instance).mro()
        next_cls = mro[mro.index(self.cls) + 1]
        attribute = getattr(next_cls, name)
        if hasattr(attribute, "__get__"):
            return attribute.__get__(self.instance, self.cls)
        return attribute
like image 77
juanpa.arrivillaga Avatar answered Sep 07 '25 20:09

juanpa.arrivillaga


The super call does not provide an instance of the superclass, but instead a representation for "self interpreted as a superclass instance". This object does not actually have the methods of the superclass, nor the attributes of self; rather, it serves to provide access to methods/attributes of the superclass/self – in other words, it is a proxy.


The result of the super call is an object that we can inspect:

class Friend(Contact):
    def __init__(self, name, phone, email):
        self.name = name
        print("super():", super())
        print(super().name)  # should work since `self` has that attribute?
        super().__init__(phone, email)

This will provide an output such as the following:

super(): <super: <class 'Friend'>, <Friend object>>
Traceback (most recent call last):
  ...
  File "/Users/so/example.py", line 10, in __init__
    print(super().name)
AttributeError: 'super' object has no attribute 'name'

Notice how it only refers to the actual class Friend and the actual instance <Friend object>¹, yet does not have the attributes set on self. The super object is not an instance of Contact, neither a new one nor from casting self.

Instead, accessing a method on the super object results in an active search of the Method Resolution Order of self, which means looking at the superclasses of Friend and dynamically binding self to them. The super object itself never actually has these methods, it merely acts as a proxy to search them.


¹It is important that the self and __class__ bound to super only satisfy isinstance(self, __class__) but not always type(self) == __class__. self defines the MRO, but __class__ defines at what point to start searching the MRO. This ensures that super works properly when subclassing.

like image 39
MisterMiyagi Avatar answered Sep 07 '25 21:09

MisterMiyagi