What class is super(classname, instance) actually calling? - python

I've seen a bunch of the python method resolution order questions on Stack Overflow, many of which are excellently answered. I have one that does not quite fit.
When requesting super(MyClassName, self).method_name, I get a type that is not returned by the (single) parent class. Putting debug into the parent class shows that it isn't hit.
I would add some code snippets, but the codebase is massive. I have been into every class listed from MyClassName.__mro__ (which tells us what the method resolution order is) and NONE of them return the type I'm getting. So the question is...
What tool or attribute in Python can I use to find out what code is actually being called so that if this happens again I can easily find out what is actually being called? I ended up finding the solution, but I'd rather know how to tackle it in a less labour intensive manner.

You can use e.g. inspect.getmodule to, per its documentation:
Try to guess which module an object was defined in.
A simple example, with a.py:
class Parent(object):
def method(self):
return True
and b.py:
import inspect
from a import Parent
class Child(Parent):
def method(self):
parent_method = super(Child, self).method # get the parent method
print "inherited method defined in {}".format(
inspect.getmodule(parent_method), # and find out where it came from
)
return parent_method()
if __name__ == '__main__':
Child().method()
Running b.py gives the result:
Parent defined in <module 'a' from 'C:/Python27\a.py'>

I think you might be getting confused between what a bound method is, and what the method resolution order is.
The returned method still counts as a bound method of the class of the actual object, even if function the method derives from is found on the parent class. This is because the method has been bound to an instance of the child class as opposed to the parent class.
eg.
class A:
def f(self):
return "A"
class B(A):
def g(self):
return super().f
class C(B):
def f(self):
return "C"
c = C()
method = c.g()
print(method) # prints <bound method C.f of <__main__.C object at 0x02D4FA10>>
print(method()) # prints A
In this instance, c.g() returns the function A.f bound to an instance of C.
To find the actual function that the bound method will call just examine the __func__ attribute:
assert method.__func__ is A.f

Related

Proper form to call a method in Python?

I was trying some examples on class inheritance and stumbled, on how to access the methods. In tutorials, the proper form is always Class().method(), however apparently, that's not the only solution.
class A():
def open(self):
print("class A method open")
class B():
def open(self):
print("class B method open")
def close(self):
print("class B method close")
class D(A, B):
def end_reached(self):
print("end reached")
## Why do both of the following lines work & produce the same output?
D().open()
D.open(1)
I expected the last line to give an error, however, the output is the same as from the line above. It did give an error on missing parameter, if the method gets called like D.open(), but giving it just any parameter works.
Is there any difference between the two lines?
The difference lays in the descriptor protocol, specifically in how descriptors provide Python's object-oriented features.
open is a class attribute of various classes; in each case, though, it has the type function. That type implements the descriptor protocol, via the definition of function.__get__, by returning either a function object or a method object, depending on whether open is accessed via the class itself or an instance of the class.
Instance lookup
Given
d = D()
the attribute lookup d.open is equivalent to
type(d).__dict__['open'].__get__(d, D)
which returns a method which wraps the original function. When called, the method passes a reference to d and any of its own arguments to the wrapped function.
f = d.open # The return value of __get__
f() # open(d)
Class lookup
The attribute lookup D.open is equivalent to
D.__dict__['open'].__get__(None, D)
which returns the function open itself.
f = D.open # The return value of __get__
f(1) # open(1)
In order to learn about this, I have to go in depth about the properties of functions and methods.
Accessing a class field of a class via its instances checks if there are __set__ and/or __get__ methods. If they are, the call happens via __get__.
Functions work this way: if a function is a class field of a class (in short, methods), they are called via function.__get__(object)(*args, **kwargs).
And here, we have a difference between Python 2 and Python 3:
Python 2 unconditionally prepends the given object to the argumends and calls the underlying function with function(object, *args, **kwargs). object gets assigned to self then, which by convention is the name of the first parameter of a method.
Python 3 checks if the given object is an instance of the class as it should be. If it isn't, we get an exception. If it is, the call gets processed as above.
Example:
def outer_f(self):
return "f called with", self
class C(object):
f = outer_f
def g(self):
return "g called with", self
c = C()
print(outer_f(c))
print(C.f(c))
print(C.f.__get__(c)())
print(c.f())
print(C.g(c))
print(C.g.__get__(c)())
print(c.g())
class A():
def open(self):
print(self)
print("class A method open")
class B():
def open(self):
print("class B method open")
def close(self):
print("class B method close")
class D(A, B):
def end_reached(self):
print("end reached")
## Why do both of the following lines work & produce the same output?
D().open() # Okay
D.open(1) # BAD
output
<__main__.D object at 0x0045FE90>
class A method open
1
class A method ope
You might want to look at a static method
class A():
#staticmethod
def open():
print("open ran")
class D(A):
def end_reached(self):
print("end reached")
D.open() # Good
output
open ran
That's not an inheritance problem.
As you can see in your method declaration, first argument is self.
D() just creates an object of class D. If you do anything on the object, that object is automatically passed as first argument.
However, when doing D.open(1) you don't invoke the method on the object, so the first argument in the method is first argument passed to it == your code assumes self=1. Because your methods behave like static methods (you don't use self inside them), you don't access self's inside, so the code doesn't break.
But if your open method invoked anything from self (another method or a field),
then you would get an attribute error - because int object probably doesn't have a method like that.
Examples from build-in classes:
"aaa".split()
str.split("aaa")
They both behave the same.
"aaa" is of type str, so interpreted looks into class str for the method and automatically passes "aaa" as self. (Just like your D() is of type D. And d=D(), then d.open() will still know the type.)
The other one shows which class to ask for the method (str) but still needs an argument self. The way we define methods shows that "self" is really an argument as well, so we can just pass it.

Python super() can't call parent's inherited method

I have a question in Python that seems very complex to me, that combines inheritance, recursion, and the super() function.
First of all, I am using Python 3, and I have a structure of deep inheritance.
In the very first parent class I declare a method, and I want that method to be called from each child class in the hierarchy, but with different inputs for each of them.
The use of that structure seems very pythonic to me and it really saves me from a lot of code repetition.
A simplified sample of my code is shown below:
class ClassA(object):
def __init__(self):
self.attr = 'a'
#classmethod
def _classname(cls):
return cls.__name__
def method1(self):
if self._classname() != 'ClassA': #don't call it for the very first parent class
super().method1()
print(self.attr)
class ClassB(ClassA):
def __init__(self):
self.attr = 'b'
class ClassC(ClassB):
def __init__(self):
self.attr = 'c'
inst1 = ClassC()
inst1.method1()
I expect that code to print
'a'
'b'
'c'
Instead it raises an attribute error:
super().method1()
AttributeError: 'super' object has no attribute 'method1'
I know that it is a complex problem, but I have tried to divide it. I tried to remove the recursion part, but I do not get any better.
Based on the various attempts I have done, I believe that I am very close to the cause of the problem, and it seems to me like a syntax problem or something that simple.
Thanks!!
I'm afraid you have built up the wrong mental model on how Python instances and classes relate. Classes only provide a series of attributes for instances to 'inherit', not separate namespaces for instance attributes to live in. When you look up an attribute on an instance and the attribute doesn't exist on the instance itself, a search is done of the classes that back the instance, with the 'nearest' class with that attribute winning over others. super() just lets you reach attributes with the same name but defined on a next class in that same layered search space.
In order for super() to work correctly, Python records what class the method1 function was defined on. Here that's ClassA, and super() will only find attributes on the parent classes of ClassA. In your example, ClassC and ClassB had already been searched and they didn't have a method1 attribute, so ClassA.method1 is being used, but there is no further method1 attribute in the rest of the layers that are searched (only object remains, and there is no object.method1).
You don't need to use super() when subclasses are not overriding a method, nor can you do what you want with super() anyway. Note that the ClassB and ClassC subclasses do not get a copy of the method at all, there is no ClassC.method1 direct attribute that needs to account for ClassB.method1 to exist, etc. Again, what happens when looking up attributes on an instance is that all class objects in the inheritance hierarchy of the instance are inspected for that attribute, in a specific order.
Take a look at your subclasses:
>>> inst1
<__main__.ClassC object at 0x109a9dfd0>
>>> type(inst1)
<class '__main__.ClassC'>
>>> type(inst1).__mro__
(<class '__main__.ClassC'>, <class '__main__.ClassB'>, <class '__main__.ClassA'>, <class 'object'>)
The __mro__ attribute gives you the method resolution order of your ClassC class object; it is this order that attributes are searched for, and that super() uses to further search for attributes. To find inst1.method, Python will step through each of the objects in type(inst1).__mro__ and will return the first hit, so ClassA.method1.
In your example, you used super() in the ClassA.method1() definition. Python has attached some information to that function object to help further searches of attributes:
>>> ClassA.method1.__closure__
(<cell at 0x109a3fee8: type object at 0x7fd7f5cd5058>,)
>>> ClassA.method1.__closure__[0].cell_contents
<class '__main__.ClassA'>
>>> ClassA.method1.__closure__[0].cell_contents is ClassA
True
When you call super() the closure I show above is used to start a search along the type(self).__mro__ sequence, starting at the next object past the one named in the closure. It doesn't matter that there are subclasses here, so even for your inst1 object everything is skipped an only object is inspected:
>>> type(inst1).__mro__.index(ClassA) # where is ClassA in the sequence?
2
>>> type(inst1).__mro__[2 + 1:] # `super().method1` will only consider these objects, *past* ClassA
(<class 'object'>,)
At no point are ClassB or ClassC involved here anymore. The MRO depends on the class hierarchy of the current instance, and you can make radical changes when you start using multiple inheritance. Adding in extra classes into a hierarchy can alter the MRO enough to insert something between ClassA and object:
>>> class Mixin(object):
... def method1(self):
... print("I am Mixin.method1!")
...
>>> class ClassD(ClassA, Mixin): pass
...
>>> ClassD.__mro__
(<class '__main__.ClassD'>, <class '__main__.ClassA'>, <class '__main__.Mixin'>, <class 'object'>)
>>> ClassD.__mro__[ClassD.__mro__.index(ClassA) + 1:]
(<class '__main__.Mixin'>, <class 'object'>)
ClassD inherits from ClassA and from Mixin. Mixin inherits from object too. Python follows some complicated rules to put all classes in the hierarchy into a logical linear order, and Mixin ends up between ClassA and object because it inherits from the latter, and not the former.
Because Mixin is injected into the MRO after ClassA, calling Class().method1() changes how super().method1() behaves, and suddenly calling that method will do something different:
>>> ClassD().method1()
I am Mixin.method1!
a
Remember, it helps to see classes as a layered search space for attributes on instances! instance.attribute is searched for along the classes if the attribute doesn't exist on the instance itself. super() just let you search for the same attribute along the remainder of that search space.
This lets you reuse method implementations when implementing a method with the same name in a subclass. That's the whole point of super()!
There are other problems with your code.
When looking up methods, they are bound to the object they were looked up on. instance.method binds the method to instance, so that when you call instance.method(), Python knows what to pass into the method as self. For classmethod objects, self is replaced with type(self), unless you did ClassObject.attribute, at which point ClassObject is used.
So your _classname method will always be producing ClassC for inst1, as the cls object that is passed in is that of the current instance. super() doesn't change what class classmethods are bound to when accessed on an instance! It'll always be type(self).
You also forgot to call super() in the ClassB and ClassC __init__ methods, so for inst1, only ClassC.__init__ is ever actually used. The ClassB.__init__ and ClassC.__init__ implementations are never called. You'd have to add a call to super().__init__() in both for that to happen, at which point there are three self.attr = ... assignments on the same instance, and only the one that executes last will remain. There is no separate self for each of the classes that make up the code for the instance, so there are no separate self.attr attributes with different values.
Again, that's because inst1.__init__() is called, __init__ is bound to inst for the self argument, and even if you used super().__init__() the self that is passed on remains inst1.
What you want to achieve is something entirely different from an attribute search across all of the classes. Printing all class names can be done with a loop over __mro__ instead:
class ClassA(object):
def method2(self):
this_class = __class__ # this uses the same closure as super()!
for cls in type(self).__mro__:
print(cls.__name__)
if cls is this_class:
break
class ClassB(ClassA): pass
class ClassC(ClassB): pass
This then produces:
>>> inst1 = ClassC()
>>> inst1.method2()
ClassC
ClassB
ClassA
If you have to print 'c', 'b', 'a' you can add extra attributes to each class:
class ClassA(object):
_class_attr = 'a'
def method2(self):
this_class = __class__ # this uses the same closure as super()!
for cls in type(self).__mro__:
if hasattr(cls, '_class_attr'):
print(cls._class_attr)
class ClassB(ClassA):
_class_attr = 'b'
class ClassC(ClassB):
_class_attr = 'c'
and you'll get
c
b
a
printed.
Thats because you're trying to access Method1() on Object_class
def method1(self):
# this will print the class name you're calling from, if you are confused.
print(self._classname())
print(self.attr)
You've written an if-else which satisfies when you're instantialize with Class C and access method1().
To print a,b,c, you will need override the method in every class.. and calling super from them.
However, this will still have a problem. Because the self attribute carries the instance of Class C, not Class B or A. So, they can't access the attribute that you're initializing in the init func.
Final code looks like this
class ClassA(object):
def __init__(self):
self.attr = 'a'
#classmethod
def _classname(cls):
return cls.__name__
def method1(self):
print(self._classname())
#don't call it for the very first parent class
#super().method1()
print(self.attr)
class ClassB(ClassA):
def __init__(self):
self.attr = 'b'
def method1(self):
print(self._classname())
print(self.attr)
super().method1()
class ClassC(ClassB):
def __init__(self):
self.attr = 'c'
def method1(self):
print(self._classname())
print(self.attr)
super().method1()
inst1 = ClassC()
inst1.method1()

How do overridden method calls from base-class methods work?

According to the docs on inheritance:
Derived classes may override methods of their base classes. Because methods have no special privileges when calling other methods of the same object, a method of a base class that calls another method defined in the same base class may end up calling a method of a derived class that overrides it.
How does that happen? Can someone illustrate this concept with a simple example?
Here's the example you requested. This prints chocolate.
class Base:
def foo(self):
print("foo")
def bar(self):
self.foo()
class Derived(Base):
def foo(self):
print("chocolate")
d = Derived()
d.bar() # prints "chocolate"
The string chocolate is printed instead of foo because Derived overrides the foo() function. Even though bar() is defined in Base, it ends up calling the Derived implementation of foo() instead of the Base implementation.
How does it work?
When an attribute look-up is performed on an instance of the class, the class dictionary and the dictionaries of its base classes are searched in a certain order (see: Method Resolution Order) for the appropriate method. What is found first is going to get called.
Using the following Spam example:
class Spam:
def produce_spam(self):
print("spam")
def get_spam(self):
self.produce_spam()
class SuperSpam(Spam):
def produce_spam(self):
print("super spam")
Spam defines the functions produce_spam and get_spam. These live in its Spam.__dict__ (class namespace). The sub-class SuperSpam, by means of inheritance, has access to both these methods. SuperSpam.produce_spam doesn't replace Spam.produce_spam, it is simply found first when the look-up for the name 'produce_spam' is made on one of its instances.
Essentially, the result of inheritance is that the dictionaries of any base classes are also going to get searched if, after an attribute look-up on the sub-class is made, the attribute isn't found in the sub-class's dictionary.
When the function get_spam is first invoked with:
s = SuperSpam()
s.get_spam()
the sequence of events roughly goes like this:
Look into SuperSpams __dict__ for get_spam.
Since it isn't found in SuperSpams __dict__ look into the dictionaries of it's base classes (mro chain).
Spam is next in the mro chain, so get_spam is found in Spam's dictionary.
Now, when produce_spam is looked up in the body of get_spam with self.produce_spam, the sequence is much shorter:
Look into SuperSpam's (self) __dict__ for produce_spam.
Find it, get it and call it.
produce_spam is found in the __dict__ first so that gets fetched.
class Base():
def m1(self):
return self.m2()
def m2(self):
return 'base'
class Sub(Base):
def m2(self):
return 'sub'
b = Base()
s = Sub()
print(b.m1(), s.m1())
prints "base sub"
To illustrate how it works consider these two classes:
class Parent(object):
def eat(self):
print("I don't want to eat that {}.".format(self.takefrompocket()))
def takefrompocket(self):
return 'apple'
def __getattribute__(self, name):
print('Looking for:', name)
method_to_use = object.__getattribute__(self, name)
print('Found method:', method_to_use)
return method_to_use
class Child(Parent):
def takefrompocket(self):
return 'salad'
The __getattribute__ method is responsible in new-style-classes (like all classes in python3) for the attribute lookup. It is just implemented to print what each lookup does - normally you don't want to and shouldn't implement it yourself. The lookup follows pythons method resolution order (MRO) just if you are really interested.
>>> some_kid = Child()
>>> some_kid.eat()
Looking for: eat
Found method: <bound method Parent.eat of <__main__.Child object at 0x0000027BCA4EEA58>>
Looking for: takefrompocket
Found method: <bound method Child.takefrompocket of <__main__.Child object at 0x0000027BCA4EEA58>>
I don't want to eat that salad.
So when you want to use eat then it uses Parent.eat in this example. But self.takefrompocket is used from Child.
>>> some_parent = Parent()
>>> some_parent.eat()
Looking for: eat
Found method: <bound method Parent.eat of <__main__.Parent object at 0x0000027BCA4EE358>>
Looking for: takefrompocket
Found method: <bound method Parent.takefrompocket of <__main__.Parent object at 0x0000027BCA4EE358>>
I don't want to eat that apple.
Here both methods are taken from Parent. Inherited classes don't (generally) interfere with their ancestors!
If your child class doesn't implement the method, raise an exception!
class Base(object):
def something (self):
raise ('Not implemented')

How do I call the parent method of a class inside completely non-related class?

I want to use the superclass to call the parent method of a class while using a different class.
Class AI():
...
for i in self.initial_computer_group:
if i.rect.x == current_coords[0] and i.rect. y== current_coords[1]:
i.move(coords_to_move[0], coords_to_move[1])
i.move() calls a method from an inherited class, when I want the original method from the parent class.
self.initial_computer_group contains a list of objects which are completely unrelated to the AI class.
I know I need to somehow get the class name of the current object i references to, but then I don't know what to use as the second argument in super() as i can't use self, since it's unrelated to AI.
So how do I use super() when I'm in a completely different class to what super is meant to call?
Note: I want to call the parent method as it speeds everything up. I only designed the inherited method to ensure the human isn't breaking the rules in this chess game.
EDIT: I found a solution by changing the name of the inherited method to something else, but I was wondering whether there's still a special way to invoke super() to solve the problem
It sounds like you want to call a specific class's method, no matter what the inheritance graph looks like (and in particular, even if that method happens to be overridden twice). In that case, you don't want super. Instead, call the class's method directly. For example, assuming the version you want is in the Foo class:
Foo.move(i, coords_to_move[0], coords_to_move[1])
As it's hard to read code in comments, here's a simple example:
class BaseClass():
def func(self):
print("Here in BaseClass.")
class InheritedClass(BaseClass):
def func(self):
print("Here in InheritedClass.")
def func(instance):
super(InheritedClass, instance).func()
In use:
>>> func(InheritedClass())
Here in BaseClass.
But this clearly makes your code less flexible (as the instance argument must be an InheritedClass instance), and should generally be avoided.
Given some inheritance hierarchy:
class Super: # descends from object
def func():
return 'Super calling'
class Base(Super):
def func():
return 'Base calling'
class Sub(Base):
def func():
return 'Sub calling'
You can get the resolution hierarchy with the __mro__ attribute:
>>> s=Sub()
>>> s.__class__.__mro__
(<class '__main__.Sub'>, <class '__main__.Base'>, <class '__main__.Super'>, <class 'object'>)
Then you can pick among those by index:
>>> s.__class__.__mro__[-2]
<class '__main__.Super'>
>>> s.__class__.__mro__[-2].func()
Super calling
You can get a specific name by matching against the __name__ attribute:
def by_name(inst, tgt):
for i, c in enumerate(inst.__class__.__mro__):
if c.__name__==tgt:
return i
return -1
Then if you want to call the parent class of an unrelated class, just use one of these methods on an instance of the descendant class with the method of interest.
Of course the simplest answer is if you know the class and method you want, just call it directly:
>>> Super.func()
Super calling
>>> Base.func()
Base calling
If you need to go several levels up (or an unknown number of levels up) to find the method, Python will do that for you:
class Super:
def func():
return 'Super calling'
class Base(Super):
pass
class Sub(Base):
pass
>>> Sub.func()
Super calling

Python multiple inheritance: Whats wrong doing it dynamically?

Based on this answer, of how __new__ and __init__ are supposed to work in Python,
I wrote this code to dynamically define and create a new class and object.
class A(object):
def __new__(cls):
class C(cls, B):
pass
self = C()
return self
def foo(self):
print 'foo'
class B(object):
def bar(self):
print 'bar'
a = A()
a.foo()
a.bar()
Basically, because the __new__ of A returns a dynamically created C that inherits A and B, it should have an attribute bar.
Why does C not have a bar attribute?
Resolve the infinite recursion:
class A(object):
def __new__(cls):
class C(cls, B):
pass
self = object.__new__(C)
return self
(Thanks to balpha for pointing out the actual question.)
Since there is no actual question in the question, I am going to take it literally:
Whats wrong doing it dynamically?
Well, it is practically unreadable, extremely opaque and non-obvious to the user of your code (that includes you in a month :P).
From my experience (quite limited, I must admit, unfortunately I don't have 20 years of programming under the belt), a need for such solutions indicates, that the class structure is not well defined, - means, there's almost always a better, more readable and less arcane way to do such things.
For example, if you really want to define base classes on the fly, you are better off using a factory function, that will return appropriate classes according to your needs.
Another take on the question:
Whats wrong doing it dynamically?
In your current implementation, it gives me a "maximum recursion depth exceeded" error. That happens, because A.__new__ calls itself from within itself indefinitely (since it inherits from itself and from B).
10: Inside A.__new__, "cls" is set to <class '.A'>. Inside the constructor you define a class C, which inherits from cls (which is actually A) and another class B. Upon instantiating C, its __new__ is called. Since it doesn't define its own __new__, its base class' __new__ is called. The base class just happens to be A.
20: GOTO 10
If your question is "How can I accomplish this" – this works:
class A(object):
#classmethod
def get_with_B(cls):
class C(B, cls):
pass
return C()
def foo(self):
print 'foo'
class B(object):
def bar(self):
print 'bar'
a = A.get_with_B()
a.foo()
a.bar()
If your question is "Why doesn't it work" – that's because you run into an infinite recursion when you call C(), which leads to A.__new__ being called, which again calls C() etc.

Categories