Puzzle about super() method - python

I am trying to understand how the super() method works. Here is the sample code I am using:
class A(object):
def __init__(self):
self.n = 'A'
def func(self):
print('#A')
self.n += 'A'
class B(A):
def __init__(self):
self.n = 'B'
def func(self):
print('#B')
self.n += 'B'
class C(A):
def __init__(self):
self.n = 'C'
def func(self):
print('#C')
super().func()
self.n += 'C'
class D(C, B):
def __init__(self):
self.n = 'D'
def func(self):
print('#D')
super().func()
self.n += 'D'
print(D.mro())
d = D()
d.func()
print(d.n)
The correct output results are:
[<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
DBCD
What I do not understand is why there is no A in the output? I thought when entering Class C, there is a super().func() which calls the func in Class A. However, it is not the case.
Can anyone help me understand this behavior?
Thank you!

super().func isn’t the parent’s func method – it’s the method of the next class in the MRO. No A appears because B’s func does not call super().func, so the chain stops at B.

No, each super().func() call lets you call func in the next class in the MRO of self (which may not be the same as the MRO of the current class). When you call C.func on an instance of D, you follow D's MRO and see that after C comes B.
B.func doesn't call super, so the chain ends there, skipping A. If you want B.func to be useable in multiple inheritance situations, it needs to call super().func() too, like the other classes do.

super doesn't just call the parent class. It ensures that the classes in a multiple inheritance tree are called in the proper order.
In your case, the super in D calls C. The super in C calls B, but because B doesn't have a call to super, the chain breaks down.

Related

Conditional Inheritance based on arguments in Python

Being new to OOP, I wanted to know if there is any way of inheriting one of multiple classes based on how the child class is called in Python. The reason I am trying to do this is because I have multiple methods with the same name but in three parent classes which have different functionality. The corresponding class will have to be inherited based on certain conditions at the time of object creation.
For example, I tried to make Class C inherit A or B based on whether any arguments were passed at the time of instantiating, but in vain. Can anyone suggest a better way to do this?
class A:
def __init__(self,a):
self.num = a
def print_output(self):
print('Class A is the parent class, the number is 7',self.num)
class B:
def __init__(self):
self.digits=[]
def print_output(self):
print('Class B is the parent class, no number given')
class C(A if kwargs else B):
def __init__(self,**kwargs):
if kwargs:
super().__init__(kwargs['a'])
else:
super().__init__()
temp1 = C(a=7)
temp2 = C()
temp1.print_output()
temp2.print_output()
The required output would be 'Class A is the parent class, the number is 7' followed by 'Class B is the parent class, no number given'.
Thanks!
Whether you're just starting out with OOP or have been doing it for a while, I would suggest you get a good book on design patterns. A classic is Design Patterns by Gamma. Helm. Johnson and Vlissides.
Instead of using inheritance, you can use composition with delegation. For example:
class A:
def do_something(self):
# some implementation
class B:
def do_something(self):
# some implementation
class C:
def __init__(self, use_A):
# assign an instance of A or B depending on whether argument use_A is True
self.instance = A() if use_A else B()
def do_something(self):
# delegate to A or B instance:
self.instance.do_something()
Update
In response to a comment made by Lev Barenboim, the following demonstrates how you can make composition with delegation appear to be more like regular inheritance so that if class C has has assigned an instance of class A, for example, to self.instance, then attributes of A such as x can be accessed internally as self.x as well as self.instance.x (assuming class C does not define attribute x itself) and likewise if you create an instance of C named c, you can refer to that attribute as c.x as if class C had inherited from class A.
The basis for doing this lies with builtin methods __getattr__ and __getattribute__. __getattr__ can be defined on a class and will be called whenever an attribute is referenced but not defined. __getattribute__ can be called on an object to retrieve an attribute by name.
Note that in the following example, class C no longer even has to define method do_something if all it does is delegate to self.instance:
class A:
def __init__(self, x):
self.x = x
def do_something(self):
print('I am A')
class B:
def __init__(self, x):
self.x = x
def do_something(self):
print('I am B')
class C:
def __init__(self, use_A, x):
# assign an instance of A or B depending on whether argument use_A is True
self.instance = A(x) if use_A else B(x)
# called when an attribute is not found:
def __getattr__(self, name):
# assume it is implemented by self.instance
return self.instance.__getattribute__(name)
# something unique to class C:
def foo(self):
print ('foo called: x =', self.x)
c = C(True, 7)
print(c.x)
c.foo()
c.do_something()
# This will throw an Exception:
print(c.y)
Prints:
7
foo called: x = 7
I am A
Traceback (most recent call last):
File "C:\Ron\test\test.py", line 34, in <module>
print(c.y)
File "C:\Ron\test\test.py", line 23, in __getattr__
return self.instance.__getattribute__(name)
AttributeError: 'A' object has no attribute 'y'
I don't think you can pass values to the condition of the class from inside itself.
Rather, you can define a factory method like this :
class A:
def sayClass(self):
print("Class A")
class B:
def sayClass(self):
print("Class B")
def make_C_from_A_or_B(make_A):
class C(A if make_A else B):
def sayClass(self):
super().sayClass()
print("Class C")
return C()
make_C_from_A_or_B(True).sayClass()
which output :
Class A
Class C
Note: You can find information about the factory pattern with an example I found good enough on this article (about a parser factory)

Python: Overriding method in child class skipping one of the Base Class

I have two base classes A and B each of them have a method myfunc, which prints out a different character.
class A:
def myfunc(self):
print('A')
class B:
def myfunc(self):
print('B')
I have one more class C which is inheriting from A and B both. In class C I have overridden myfunc and called super over it.
class C(B, A):
def myfunc(self):
super().myfunc()
Now if I execute following code, it prints only one character
x = C()
x.myfunc()
Output:
B
I tried print(C.__mro__) which gives me (<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>) So it should go to class A and print character A also. right?
Also if I switch the order of inheritance like C(A,B) it and use the same code , it is skipping class B.
My questions:
Why it's skipping class A?
How to execute myfunc method in both classes A and B
I looked up similar discussion but found it confusing.
Answering question 1:
It is skipping myFunc on class A because what python does when you call super.myFunc() is search for the first function with name myFunc in all the base classes (in this case B and A) and it does that in order, so it first look for myFunc in class B. So, because there is a function named myFunc in class B python stop searching and executes only that.
Answering question 2:
There is a trivial way of doing that which is this:
class A:
def f(self):
print('A')
class B:
def f(self):
print('B')
class C(B, A):
def f(self):
A.f(self)
B.f(self)
c = C()
c.f()
Basically you can call any function of any class and then pass the instance as the first argument (self). I believe you can't execute both functions with only one call, you will need to make a call for each one.

Method order resolving in multiple and multilevel inheritance in python while calling the constructor

Notes:
Getting the right inheritance order while printing the MRO of class D but not get getting the constructor call of class C.
Question: Why not printing C Constructor after A Constructor in the given code below?:
class A(object):
def __init__(self):
print("A Constructor")
class B(A):
def __init__(self):
print("B Constructor")
super(B, self).__init__()
class C():
def __init__(self):
print("C Constructor")
super().__init__()
def method(self):
print("C method")
class D(B, C):
def __init__(self):
print("D Constructor")
super(D, self).__init__()
super().method()
d = D()
print(D.__mro__)
Output:
1 The init of C never been called cos
2 super starts searching on D goes D->B->A-> and ends on -> object
3 In this case the search starts D->B->A->object and the first super is done.
The object-or-type determines the method resolution order to be searched. The search starts from the class right after the type.
If mro of object-or-type is D -> B -> C -> A -> object and the value of type is B, then super() searches C -> A -> object
4 then second super() gives you access to methods in a superclass from the subclass that inherits from it.
5 In this case gives you access the the C.method and prints oue "C method" after the constructors of the first super are printed.
super() alone returns a temporary object of the superclass that then allows you to call that superclass’s methods
allows you to swap out superclasses with minimal code changes.
if you want to know how MRO works in Python, I suggest you see this link.
In Python 2.3 and later, Python uses the C3 Linearization algorithm for the MRO.
Good luck.

How do I get the instance method's next-in-line parent class from `super()` in Python

I'd like to know the type of an instance obtained from super() function. I tried print(super()) and __print(type(super()))__
class Base:
def __init__(self):
pass
class Derive(Base):
def __init__(self):
print(super())
print(type(super()))
super().__init__()
d = Derive()
The result is
<super: <class 'Derive'>, <Derive object>>
<class 'super'>
With those result, I was wondering how super().__init__() calls the correct constructor.
You can't do what you want with super() directly. Go to the class MRO (see class.__mro__) instead:
class Derive(Base):
def __init__(self):
mro = type(self).__mro__
parent = mro[mro.index(__class__) + 1]
print(parent)
Here __class__ is the magic closure variable* that references the class the current function was defined in; the above continues to work even when you subclass or mix in additional classes with Derive, even when you produce a diamond inheritance pattern.
Demo:
>>> class Base: pass
...
>>> class Derive(Base):
... def __init__(self):
... mro = type(self).__mro__
... parent = mro[mro.index(__class__) + 1]
... print(parent)
...
>>> Derive()
<class '__main__.Base'>
<__main__.Derive object at 0x10f7476a0>
>>> class Mixin(Base): pass
...
>>> class Multiple(Derive, Mixin): pass
...
>>> Multiple()
<class '__main__.Mixin'>
<__main__.Multiple object at 0x10f747ba8>
Note how the Multiple class inherits from both Derive and Mixin, and the next class in the MRO is thus found to be Mixin, not Base, because Mixin also derives from Base.
This copies what super() does; find the next class in the MRO for the instance, relative to the current class.
* For background, see Why is Python 3.x's super() magic?
From your comments, you want to know how super knows which method to call next. Super inspects the mro of the instance, knows the current class method it's in, and calls the next one in line. The following demo will work in Python 2 and 3, and in Python 2, it prints the name of each class thanks to the metaclass, so I'll use that output:
First the imports and setup to make the printing nicer:
import inspect
class Meta(type):
def __repr__(cls):
return cls.__name__
Next, we define a function to tell us what's going on based on the super object itself
def next_in_line(supobj):
print('The instance class: {}'.format(supobj.__self_class__))
print('in this class\'s method: {}'.format(supobj.__thisclass__))
mro = inspect.getmro(supobj.__self_class__)
nextindex = mro.index(supobj.__thisclass__) + 1
print('super will go to {} next'.format(mro[nextindex]))
Finally, we declare a class hierarchy based on the example from the wikipedia entry on C3 linearization, for a sufficiently complex example, note the metaclass repr doesn't work in Python3, but the attribute assignment won't break it. Also note that we use the full super call of super(Name, self) which is equivalent to super() in Python 3, and will still work:
class O(object):
__metaclass__ = Meta
def __init__(self):
next_in_line(super(O, self))
super(O, self).__init__()
class A(O):
def __init__(self):
next_in_line(super(A, self))
super(A, self).__init__()
class B(O):
def __init__(self):
next_in_line(super(B, self))
super(B, self).__init__()
class C(O):
def __init__(self):
next_in_line(super(C, self))
super(C, self).__init__()
class D(O):
def __init__(self):
next_in_line(super(D, self))
super(D, self).__init__()
class E(O):
def __init__(self):
next_in_line(super(E, self))
super(E, self).__init__()
class K1(A, B, C):
def __init__(self):
next_in_line(super(K1, self))
super(K1, self).__init__()
class K2(D, B, E):
def __init__(self):
next_in_line(super(K2, self))
super(K2, self).__init__()
class K3(D, A):
def __init__(self):
next_in_line(super(K3, self))
super(K3, self).__init__()
class Z(K1, K2, K3):
def __init__(self):
next_in_line(super(Z, self))
super(Z, self).__init__()
Now when we print the mro of Z, we get the method resolution order defined by this algorithm applied to the inheritance tree:
>>> print(inspect.getmro(Z))
(Z, K1, K2, K3, D, A, B, C, E, O, <type 'object'>)
And when we call Z(), because our function uses the mro, we'll visit each method in order:
>>> Z()
The instance class: Z
in this class's method: Z
super will go to K1 next
The instance class: Z
in this class's method: K1
super will go to K2 next
The instance class: Z
in this class's method: K2
super will go to K3 next
The instance class: Z
in this class's method: K3
super will go to D next
The instance class: Z
in this class's method: D
super will go to A next
The instance class: Z
in this class's method: A
super will go to B next
The instance class: Z
in this class's method: B
super will go to C next
The instance class: Z
in this class's method: C
super will go to E next
The instance class: Z
in this class's method: E
super will go to O next
The instance class: Z
in this class's method: O
super will go to <type 'object'> next
And we stop at object.__init__. From the above we can see that super always knows what class of the instance it is in, the class's method that it is currently in, and can deduce from the instance class's MRO where to go next.
I'd like to know the name of the base class?
If you only want the direct base (or more than one, in the case of multiple inheritance), you can use the __bases__ attribute, which returns a tuple
>>> Derive.__bases__
(<class __main__.Base at 0xffeb517c>,)
>>> Derive.__bases__[0].__name__
'Base'
I recommend the inspect module for getting the Method Resolution Order (which super follows based on the original caller's class):
>>> import inspect
>>> inspect.getmro(Derive)
(<class __main__.Derive at 0xffeb51dc>, <class __main__.Base at 0xffeb517c>)
Getting it from super
super().__self_class__ gives the instance class, and super().__thisclass__ gives us the current class. We can use the instance's MRO and look up the class that comes next. I presume you wouldn't do this in the final parent, so I'm not catching an index error:
class Base:
def __init__(self):
print(super().__self_class__)
print(super().__thisclass__)
class Derive(Base):
def __init__(self):
print(super().__self_class__)
print(super().__thisclass__)
mro = inspect.getmro(super().__self_class__)
nextindex = mro.index(super().__thisclass__) + 1
print('super will go to {} next'.format(mro[nextindex]))
super().__init__()
>>> d = Derive()
<class '__main__.Derive'>
<class '__main__.Derive'>
super will go to <class '__main__.Base'> next
<class '__main__.Derive'>
<class '__main__.Base'>

How does multiple inheritance work in descriptors?

I watched a great video on YouTube about Python metaprogramming. I tried to write the following code (which is almost the same from the video):
class Descriptor:
def __init__(self, name):
self.name = name
def __get__(self, instance, cls):
return instance.__dict__[self.name]
def __set__(self, instance, val):
instance.__dict__[self.name] = val
def __delete__(self, instance):
del instance.__dict__[self.name]
class Type(Descriptor):
ty = object
def __set__(self, instance, val):
if not isinstance(val, self.ty):
raise TypeError("%s should be of type %s" % (self.name, self.ty))
super().__set__(instance, val)
class String(Type):
ty = str
class Integer(Type):
ty = int
class Positive(Descriptor):
def __set__(self, instance, val):
if val <= 0:
raise ValueError("Must be > 0")
super().__set__(instance, val)
class PositiveInteger(Integer, Positive):
pass
class Person(metaclass=StructMeta):
_fields = ['name', 'gender', 'age']
name = String('name')
gender = String('gender')
age = PositiveInteger('age')
So PositiveInteger is inherited from Integer and Positive, and both classes have __get__ method defined to do some validation. I wrote some test code to convince myself that both methods will run:
class A:
def test(self):
self.a = 'OK'
class B:
def test(self):
self.b = 'OK'
class C(A, B):
pass
c = C()
c.test()
print(self.a)
print(self.b)
Only to find that only the first print statement works. The second will raise an AttributeError, which indicates that when there's name conflict, the first base class wins.
So I wonder why both validations work? It's even more weird that when only the Integer check passes (e.g. person.age = -3), it's super().__set__(instance, val) has no effect, leaving person.age untouched.
The validation logic of both Positive and Integer runs because both Type and Positive have this line in __set__:
super().__set__(instance, val)
This doesn't skip to Descriptor.__set__. Instead, it calls the next method in method resolution order. Type.__set__ gets called, and its super().__set__(instance, val) calls Positive.__set__. Positive.__set__ runs its validation and calls Descriptor.__set__, which does the setting. This behavior is one of the reasons we have super.
If you wanted your test methods to behave like that, you would need to do two things. First, you would need to make A and B inherit from a common base class with a test method that doesn't do anything, so the super chains end at a place with a test method instead of going to object:
class Base:
def test():
pass
Then, you would need to add super().test() to both A.test and B.test:
class A(Base):
def test(self):
self.a = 'OK'
super().test()
class B(Base):
def test(self):
self.b = 'OK'
super().test()
For more reading, see Python's super() considered super.
Sorry, my bad.
The video gave perfect explanation just minute after where I paused and asked this question.
So when multiple inheritance happends, there's MRO thing (Method Resolution Order) defined in each class that determines the resolution order of methods in the super() chain.
The order is determined by depth-first search, e.g.
class A:
pass
class B(A):
pass
class C(B):
pass
class D(A):
pass
class E(C, D):
pass
E.__mro__ will be:
(<class '__main__.E'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.A'>, <class 'object'>)
One thing to notice is that A will appear in the inheritance tree multiple times, and in the MRO list it will only be in the last place where all A's appear.
Here's the trick: the call to super() won't necessarily go to its base. Instead, it'll find in the MRO list what comes next.
So to explain what happens in the code:
The super() call in Integer.__get__ (which is inherited from Type.__get__) won't go to Descriptor.__get__, because Descriptor appears last in the MRO list. It will fall into Positive.__set__, and then its super() will fall into Descriptor, which will eventually set the value of the attribute.

Categories

Resources