> That is also true if you attach the method to the class (C.foo = B.foo); you can call it like a method, but super() still checks the type and will complain.
You're right. Here's some example Python code:
>>> class A:
... def foo(self):
... print('A.foo')
>>> class B(A):
... def foo(self):
... super().foo()
... print('B.foo')
>>> b = B()
>>> b.foo()
A.foo
B.foo
>>> class C:
... def foo(self):
... print('C.foo')
>>> class D(C):
... def foo(self):
... super().foo()
... print('D.foo')
>>> d = D()
>>> d.foo()
C.foo
D.foo
Now, if I do `D.foo = B.foo` and call `d.foo()`, I do indeed get a "TypeError: super(type, obj): obj must be an instance or subtype of type". That's better than getting `A.foo B.foo`, as happens in JavaScript.However, if `super` were truly dynamic, I would have expected to get `C.foo B.foo`.
Re:Crafting Interpreters: Superclasses
by blattimwind on Wednesday March 18, 2020 @04:36PM
(#22620971)
IIRC super() is actually syntactic sugar for super(ClassTheDefIsFoundIn, nameOfFirstArgument), because there is no way I can think of to implement that as a function at runtime.
Yes, I think that’s right. However, I think it would work better if classTheDefIsFoundIn were resolved dynamically rather than lexically. Perhaps it would have been possible to do this by modifying the descriptor protocol?
Re:Crafting Interpreters: Superclasses
by blattimwind on Thursday March 19, 2020 @09:46AM
(#22627421)
I'm not sure that's possible, because you could only work with type(self), and that breaks MRO traversal. (A chain of super().x() calls in a hierarchy like A->B->C would always call the first parent class, because it has no way to tell >where< in the hierarchy it is. That's the reason why the class argument is required.)
I think that would need an entirely differently designed mechanism and can't be bolted on.
No, super() checks that "self" is an instance of the given class.
So if you take a method defined in a class hierarchy "A -> B", and attach it to an instance of "C" (i.e. c.foo = B.foo) and call it like "c.foo(c)", which you have to do, since you didn't invoke the descriptor protocol to bind the method, you will get a type error, because c is not an instance of A/B. That is also true if you attach the method to the class (C.foo = B.foo); you can call it like a method, but super() still checks the type and will complain.
Also, if you take a bound method and attach it to another object (i.e. c.foo = b.foo), it is already bound and you can't pass a "self" different from the object it's bound to. E.g. if you are passing around a callback that's a method.
Note how Class.method is just a function, while instance.method is a bound method. That's due to the descriptor protocol of functions (__get__/__set__).