Calling super() inside a class' classmethod to get at the metaclass method

super() python
super() java
super constructor javascript
call to super must be first statement in constructor
super and super() in java
super.super in java
what is super class in java
access super class variable java

Just when I though I understood metaclasses...

Disclaimer: I have looked around for an answer before posting, but most of the answers I have found are about calling super() to get at another @classmethod in the MRO (no metaclass involved) or, surprisingly, a lot of them were about trying to do something in metaclass.__new__ or metaclass.__call__ which meant the class wasn't fully created yet. I'm pretty sure (let's say 97%) that this is not one of those problems.

Environment: Python 3.7.2

The problem: I have a metaclass FooMeta that defines a method get_foo(cls), a class Foo that is built from that metaclass (so an instance of FooMeta) and has a @classmethod get_bar(cls). Then another class Foo2 that inherits from Foo. In Foo2, I subclass get_foo by declaring it a @classmethod and calling super(). This fails miserably...

i.e. with this code

class FooMeta(type):
    def get_foo(cls):
        return 5


class Foo(metaclass=FooMeta):
    @classmethod
    def get_bar(cls):
        return 3


print(Foo.get_foo)
# >>> <bound method FooMeta.get_foo of <class '__main__.Foo'>>
print(Foo.get_bar)
# >>> <bound method Foo.get_bar of <class '__main__.Foo'>>


class Foo2(Foo):
    @classmethod
    def get_foo(cls):
        print(cls.__mro__)
        # >>> (<class '__main__.Foo2'>, <class '__main__.Foo'>, <class 'object'>)
        return super().get_foo()

    @classmethod
    def get_bar(cls):
        return super().get_bar()


print(Foo2().get_bar())
# >>> 3
print(Foo2().get_foo())
# >>> AttributeError: 'super' object has no attribute 'get_foo'

The question: So with my class being an instance of the metaclass, and having verified that both class methods exist on the class Foo, why aren't both calls to the super().get_***() working inside Foo2? What am I not understanding about metaclasses or super() that's preventing me from finding these results logical?

EDIT: Further testing shows that the methods on Foo2 being class methods or instance methods doesn't change the result.

EDIT 2: Thanks to @chepner's answer, I think the problem was that super() was returning a super object representing Foo (this is verified with super().__thisclass__) and I was expecting super().get_foo() to behave (maybe even to call) get_attr(Foo, 'get_foo') behind the scene. It seems that it isn't... I'm still wondering why, but it is getting clearer :)

Foo may have a get_foo method, but super isn't designed to check what attributes a superclass has. super cares about what attributes originate in a superclass.


To understand super's design, consider the following multiple inheritance hierarchy:

class A:
    @classmethod
    def f(cls):
        return 1
class B(A):
    pass
class C(A):
    @classmethod
    def f(cls):
        return 2
class D(B, C):
    @classmethod
    def f(cls):
        return super().f() + 1

A, B, C, and D all have an f classmethod, but B's f is inherited from A. D's method resolution order, the sequence of classes checked for attribute lookup, goes (D, B, C, A, object).

super().f() + 1 searches the MRO of cls for an f implementation. The one it should find is C.f, but B has an inherited f implementation, and B is before C in the MRO. If super were to pick up B.f, this would break C's attempt to override f, in a situation commonly referred to as the "diamond problem".

Instead of looking at what attributes B has, super looks directly in B's __dict__, so it only considers attributes actually provided by B instead of by B's superclasses or metaclasses.


Now, back to your get_foo/get_bar situation. get_bar comes from Foo itself, so super().get_bar() finds Foo.get_bar. However, get_foo is provided not by Foo, but by the FooMeta metaclass, and there is no entry for get_foo in Foo.__dict__. Thus, super().get_foo() finds nothing.

Super Keyword in Java, Call to super() must be first statement in Derived(Student) Class constructor. If a constructor does not explicitly invoke a superclass constructor, the Java compiler � Part of inheritance is defining the hierarchy of object instantiation. The super () method on a class constructor allows a subclass to pass arguments and execute the constructor of its superclass. The super () method is accessed using a semicolon delimiter off the constructor method, like this: Constructor ():super ()

get_foo is not an attribute of Foo, but rather an attribute of type(Foo):

>>> 'get_foo' in Foo.__dict__
False
>>> 'get_foo' in type(Foo).__dict__
True

So while Foo.get_foo will resolve to type(Foo).get_foo, super().get_foo does not because the proxy returned by super() is similar to Foo, but is not Foo itself.

Using the Keyword super (The Java™ Tutorials > Learning the Java , Here is a subclass, called Subclass , that overrides printMethod() : public class Subclass extends Superclass { // overrides printMethod in Superclass public Here is the MountainBike (subclass) constructor that calls the superclass constructor� “[Super is used to] 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. The search order is same as that used by getattr() except that the type itself is skipped.”

Supercharge Your Classes With Python super() – Real Python, Caution: Note that in our example above, super() alone won't make the method calls for you: you have to call the method on the proxy object itself. Here you have � Call super class constructor. To call constructors from super or parent class, use super keyword. The usage of super keyword is similar to this keyword – only difference is that super refers to super class and this refers to current instance. public Employee() { //refer to Object class constructor //as it is parent class for every class super(); } Private Constructors

Calling super super class method, foo() when you specify "this" in invoke. This is a consequence from the fact that all Java methods are virtual methods. It seems you need help from the B class (e.g.� The function definitions inside a class normally have a peculiar form of argument list, dictated by the calling conventions for methods — again, this is explained later. When a class definition is entered, a new namespace is created, and used as the local scope — thus, all assignments to local variables go into this new namespace.

super() in Java, super() can be used within a constructor to call the constructor of the parent class. OK, now let's practically implement these points of super() . Check out the� The problem is that the anonymous class A itself (not the base class A) cannot be referenced inside the Runnable class. The anonymous class is represented as something like package.A$1 when compiled to its own class file. For example, when you call superMethod inside the run of the thread, the following bytecode is executed:

Super keyword in java with example, Super keyword in java with example. 1) To access the data members of parent class when both parent and child class have member with same name. 2) To explicitly call the no-arg and parameterized constructor of parent class. 3) To access the method of parent class when child class has overridden that method. Hema Latha M S wrote:Can anyone tell me the reason why cant i call super() or why cant i use super in the very first line inside the class, but we can use same inside methods or constructor. Only assignments can appear outside methods and constructors. The super() call isn't an assignment.

Comments
  • The most direct way will be return FooMeta.get_foo(cls) + 1, but it begs the question - metaclasses are for customizing creation of the class. You're not doing that. So, why are you using a metaclass? super is more about traversing the MRO (parent or sibling classes when using inheritance) so I'm not really sure why you're trying to use it to get at a metaclass method in the first place, these are quite unrelated concerns.
  • @wim I have made the code as simple as possible to illustrate the problem. Obviously there's a thousand different and simpler ways to write this logic without metaclasses, but that is not my question (TBH, the actual code that led me to ask myself and SO this question is already pushed in a simpler form, but still with a mcs, because it was needed). My question is more theoretical than practical (cf. the last sentence of the original post). Through this, I'm trying to have a better understanding of the way python works.
  • The more I test, the more I think it is my understanding of the way super() works that is at fault...
  • Your understanding seems OK so far, but I'm not clear on why would you expect super(FooMeta, Foo) to proxy to an existing get_foo method at all? Because, in absence of anything else in the inheritance chain, the "next method" here would just be attempting a method resolution on type. If you wanted to access the metaclass get_foo method, you just use FooMeta.get_foo directly.
  • @wim I have updated the example and the question. I have been doing more tests on my side so, short of finding an actual answer, I do hope I've managed to narrow the question to its essentials.
  • Thank you !!! This was the "ahaah!" moment for me :) Your example with multiple inheritance shows quite clearly why super().get_foo() can't simply do a getattr(Foo, 'get_foo') behind the scene. Thank you :)
  • This is a great answer. I was looking into the CPython source code to see if the implementation of super gave any explanation for why it works the way it does (nope). If you're curious, you can find the C code for what is effectively super.__getattribute__ here.
  • This answer, I think, requires a deeper dive into the descriptor protocol, but I don't have the time to do so.
  • Thank you, I think this is in fact a step closer to answering the question. It could just be that get_attr on a super object is not the same as get_attr on the object represented by super_object.__thisclass__. I still find the asymmetry troubling (they are both methods of Foo after all).
  • @chepner There are not descriptors here with __get__ and __set__. @classmethod is a decorator, not a descriptor.
  • @SamuelMuldoon: The objects returned by the classmethod decorator are, in fact, descriptors. They could not perform their functionality without being descriptors.
  • The whole purpose of the classmethod type is to provide an object with a different implementation of __get__.
  • You don't seem to have addressed super anywhere in this post, which is kind of strange for an answer posted on a question about super.
  • @user2357112 You can't use super to get the meta-class because a class does not inherit from a meta-class. In my answer, I explain that you can use type(class) instead of super()
  • As for your attribute-access pseudo-code on note 3 - note that descriptors featuring a __set__ or __delete__ (data descriptors) have priority, even if an attribute exists in the instance's __dict__ as well.