简体   繁体   中英

Calling super() without a base class

I am working through the O Reilly Python Cookbook and have been struggling with the below code. It is to with calling a method on a parent class using super() :

class Proxy:
    def __init__(self, obj):
        self._obj = obj

    # Delegate attribute lookup to internal obj
    def __getattr__(self, name):
        return getattr(self._obj, name)

    # Delegate attribute assignment
    def __setattr__(self, name, value):
        if name.startswith('_'):
            super().__setattr__(name, value)    # Call original __setattr__
        else:
            setattr(self._obj, name, value)

if __name__ == '__main__':
    class A:
        def __init__(self, x):
            self.x = x
        def spam(self):
            print('A.spam')

    a = A(42)
    p = Proxy(a)
    print(p.x)
    print(p.spam())
    p.x = 37
    print('Should be 37:', p.x)
    print('Should be 37:', a.x)

The book states:

In this code the implementation of __setatrr__() includes a name check. If the name starts with an underscore it invokes the original implementation of __setattr__() using super() . Otherwise, it delegates to the internally held object self._obj .

I am confused. How does super() work then if there is no explicit base class listed? What exactly then is super() referring to?

There is always a base class; with none explicitly mentioned, Proxy inherits directly from object .

Each class defines a method-resolution order, determined recursively by its base class(es) and its ancestors. When super() gets called, it resolves to a "proxy" of the next class in the MRO of self , whether or not that class appears in the MRO of the class you are currently defining.

Consider the following classes:

class A:
    def foo(self):
        print("A.foo")


class B(A):
    def foo(self):
        super().foo()
        print("B.foo")


class C(A):
    def foo(self):
        super().foo()
        print("C.foo")


class D(C):
    def foo(self):
        super().foo()
        print("D.foo")


class E(B,D):
    def foo(self):
        super().foo()
        print("E.foo")

e = E()

The MRO of E is [E, B, D, C, A, object] . When you call e.foo() , you start a chain of calls in MRO order. In particular, the call to super in B.foo does not invoke A.foo , but D.foo , a method in a class B knows nothing about, as D is not an ancestor of B . But both B and D are ancestors of E , which is what matters.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM