Ocelot20
Ocelot20

Reputation: 10800

Difference between super(Foo, self) and super(Foo, self.__class__)?

I'm extremely new to python, so hopefully I'm not missing too much requisite knowledge to understand this, but...

I have a class with some properties, and I'm trying overriding the setter on one of them similar to this example:

class Foo(object):
    def __init__(self):
        self._x = True

    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

class Bar(Foo):
    @property
    def x(self):
        return super().x

    @x.setter
    def x(self, value):
        print('x set')

        super(Bar, self.__class__).x.fset(self, value)

bar = Bar()

# Prints 'x set' as expected:
bar.x = True

This works fine, but I really don't understand why this line works the way it does...

super(Bar, self.__class__).x.fset(self, value)

...and how it differs from this line, which doesn't work:

super(Bar, self).x.fset(self, value)

Any guidance would be appreciated.

Upvotes: 3

Views: 175

Answers (1)

chepner
chepner

Reputation: 532093

To simplify things, we'll assume (as in your example) that super always returns a proxy for something defined in Foo, that is, there are no other classes involved.

  1. Ignore the call to super for the moment, consider the difference between bar.x and Bar.x. The first would invoke the getter for the named property; the latter is just a reference to the property itself.

  2. Now consider a non-property attribute. super(Bar, self).method would be an instance of a bound method, such that super(Bar, self).method(x) would be equivalent to Foo.method(self, x). super(Bar, self.__class__).method, however, would be the unbound method, i.e. simply Foo.method.

  3. Now combine steps 1 and 2. We know that bar.x will cause the getter defined in Bar to be called, and we know that super(Bar, self) is a proxy for Foo via self. Therefore, super(Bar, self).x must call the getter defined not in Bar, but in Foo.

    We also know that Bar.x is simply a reference to a property object, one that doesn't invoke its getter; and we know that super(Bar, self.__class__) acts as a proxy for Foo independent of any particular object. Therefore, we can conclude that super(Bar, self.__class__).x, for an instance self of Bar, is a reference to the property object defined in Foo.

Upvotes: 2

Related Questions