How does multiple inheritance work with super? - python

Here are the few classes let's say, B, C, D and A, while in class A I am using super method which have class 'B' as an argument.
Where classes are as defined as below:
class B:
def __init__(self):
print('B')
class C:
def __init__(self):
print('C')
class D:
def __init__(self):
print('D')
class A(B,C,D):
def __init__(self):
super(B,self).__init__()
A()
When I am trying to initiate the class A, it should invoke class B as I passed B in super method. But it is giving me output 'C'. If I am passing D in super instead of B it's not giving any output. Why? Instead it just invoke class A and leave.
Why is this happening?

The class you pass as an argument to super should be the current class whose superclass we want to look for, not the target superclass. With your class A, the method resolution order is
A < B < C < D < object
So super(A, self).__init__() would call the method on the class following A in the MRO, which is B. Likewise, super(B, self).__init__() would call the one following B, which is C. super(C, self) would give us D, and super(D, self) would give us object (Side note: I don't know what super(object, self) does, but it seems to return a proxy object that just eats all method calls).
What you're looking for is
super(A, self).__init__()
but since you're inside a method and want to call the next method in the MRO chain anyway (the default, and most sane, behavior), you can use the 0-argument form.
super().__init__()

Related

Understanding Nested Inheritance in Python

Here is a simplified code of my main code illustrating the behaviour I obtain.
Suppose I have a main class (MAIN) and two classes (A,B) inheriting from it. This main class has a method which is overwriten by A but not by B, which means that B inherits the method from main.
Then I have a class D which inherits from A and from B, and has a method which calls the aforementioned method. From what I have understood in the way multiple inheritance work, if I define D as class D(A,B) then if A and B have a shared method, calling D.method() will call A.method, and vice-versa (i.e if class D(B,A) then B.method is called. The following code exemplifies this text.
class MAIN(object):
def __init__(self):
pass
def print(self):
print('HELLO MAIN')
class A(MAIN):
def __init__(self):
pass
def print(self):
print('HELLO A')
class B(MAIN):
def __init__(self):
pass
class C(A,B):
def __init__(self):
pass
def Cprint(self):
self.print()
c = C()
c.Cprint()
class C(B,A):
def __init__(self):
pass
def Cprint(self):
self.print()
c = C()
c.Cprint()
However this code always print 'HELLO A', i.e even in the case class C(B,A) I don't get a HELLO MAIN as I would expect. What is happening here? Thanks so much in advance
The mro is (C, A, B, MAIN) with class C(A, B) and (C, B, A, MAIN) with class C(B, A). In both cases, A is before MAIN. B doesn't define .print, so it doesn't matter.
The method uplooks works like this: (pseudo code)
def find_attribute(obj, name):
if name in obj.__dict__:
return obj.__dict__[name]
mro = type(obj).__mro__
for cls in mro:
if name in cls.__dict__:
return cls.__dict__[name] # (Here a bit more magic for descriptors happens)
raise AttributeError(name)
For the classes this is what their __dict__ look like:
MAIN.__dict__ = {"print": <method MAIN.print>}
A.__dict__ = {"print": <method A.print>}
B.__dict__ = {}
C.__dict__ = {"Cprint": <method C.Cprint>}
As you can see, B does not have a print defined, so in mro=(C, B, A, MAIN) the first print that does get found is in A.
You are inheriting the Class A everywhere and class A overrrides Main functions print() thats why you dont get the "HELLO MAIN"
class C(B):
def __init__(self):
pass
def Cprint(self):
self.print()
inherit only B class which does not overrides Main class print function then you will get the HELLO MAIN output

The init values did not inherited to another class with super()

I built 2 classes. first class with its own init value and another class will inherit the init values from the first class. I am wondering whether I understand it correctly of the usage of super().
class testing(testing_2):
def __init__(self, name, c):
super().__init__(name, c)
def check(self):
print(super().name)
class testing_2:
def __init__(self, name, c):
self.name = name
self.c = c
tt = testing("tester", "check")
tt.check()
I thought my code was supposedly printed the "tester" because I initialize the testing class with name and c. since testing class inherit from testing_2 so we can just print the name. Am I confusing something?
My expectation is:
testing_2 will take the value from testing and we can print the values of testing_2 in testing.
Simplify it:
class A:
def __init__(self, name, c):
self.name = name
self.c = c
class B(A):
def check(self):
print(self.name)
tt = B("tester", "check")
tt.check()
The B object will have all the same things as an A object, since it inherits them. No need to implement __init__ on B if it doesn't do anything useful. You can access self.name directly, just as you would within A. The object that has that property is self. It's set on self and you can access it with self.
Remember, self is the object instance, not the class. When doing B(...), the self in A.__init__(self, ...) is actually an instance of B.
You only need to explicitly use super if you are overriding parent methods, like in:
def __init__(self, name, c):
super().__init__(name, c)
Here __init__ is overridden, and in order to execute the parent's __init__ you need to access it through super. Just self.__init__(name, c) would access the child's __init__ method and you'd call it in an endless recursive loop.

printing an argument from another class which inherited from different class

Apparently, I have 3 different classes; a , b and c.
class a: contain the self.king which was inherited from class b(entered by user)
class b: prompt user to enter the king value and this will be pass to class a
class c: inherit from class a that have the entered king values from class b and which was then pass to class a.
Example of codes:
class a(object):
def __init__(self, king):
self.king = king
class b(a):
def __init__(self, king):
super(b,self).__init__(king=king)
class c(a):
def __init__(self):
super(c,self).__init__()
print(self.king)
if __name__ == "__main__":
bb = b(king="madsfjsfdsdfn")
cc = c()
cc
Did I do anything wrong here? I get an error such as :
TypeError: __init__() missing 1 required positional argument: 'king'
I thought the value entered in class b has been passed to class a. Since it has been passed to class a, then class c by using super.init() will inherits its values and print it accordingly. Did I do it wrongly and understand super() function wrongly?
When user pass value and only 2 class are present:
class a(object):
def __init__(self, king):
self.king = king
class b(a):
def __init__(self, king):
super(b,self).__init__(king=king)
print(self.king)
if __name__ == "__main__":
bb = b(king="madsfjsfdsdfn")
bb
Then my output is correct:
madsfjsfdsdfn
This example I think meets your specification. The only class that needs a constructor (init) is the one where you are storing a value in an instance of the class (creating an object to store a king value).
#Child classes inherit from parent classes
class b():
def prompt_for_king():
return input("Enter king value!\n")
class a(b):
def __init__(self):
self.king = b.prompt_for_king()
class c(a):
pass
if __name__ == "__main__":
#instantiate class
object_c=c()
print (object_c.king)
This will be the most similar to my current solving problem and what I would like to get.
My goal is actually to find a way to pass this stored value which was called in class b(from my example) and passed as instance of class a and then inherit it to class c
Code:
class a(object):
def __init__(self, king):
self.yes = king
def passing(self):
return self.yes
class c(object):
def __init__(self, king):
self.yes = king
def ps(self):
print(self.yes)
class b(a,c):
def __init__(self, king):
super().__init__(king = king)
if __name__ == "__main__":
bb = b(king="aaa")
bb.ps()
You're looking at it backwards. When you define class b(a): it means that b inherits from a and not the other way around, so when you call super on c's __init__ you're calling a.
Basically what happens when you do c() (the same as c.__init__()), the first line of c's __init__ function becomes a.__init__() (the same as a()), and as the error says a's __init__ function takes the king argument and can't be called without such argument.
Besides there's a conceptual misunderstanding between Classes and Objects, for instance in your code a, b and c are classes (not to mention classes should always start with a capital letter) while bb and cc are objects. Once you define a class you won't be able to inherit attributes modified outside the class definition.
EDIT:
After reading several comments I think that what you want is a static attribute king, it's important to know that self keyword only exists within objects, so by doing self.king = king you're saying only a's objects will have a king attribute, and not a class itself.
Say we have a class A:
class A(object):
p1 = 'static'
def __init__(self):
self.p2 = 'object'
I can access A.p1 or A().p1 and get 'static', A().p2 will give me 'object' and A.p2 will give me an error because A it's not an object. If a class inherits from A it will only inherit p1, as p2 is not static.
I understand that what you want to do is to change this p1 from instanciating a class that inherits from A, say B:
class B(A):
def __init__(self, param):
super(B, self).__init__()
super.p1 = param
the thing is super is not actually the parent class, but a built in function that returns an object, so doing this yields:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-12-ca4dc9978d74> in <module>
----> 1 B(2)
<ipython-input-10-af935ee24ae2> in __init__(self, param)
2 def __init__(self, param):
3 super(B, self).__init__()
----> 4 super.p1 = param
5
6
TypeError: can't set attributes of built-in/extension type 'super'
you'd have to do
class B(A):
def __init__(self, param):
super(B, self).__init__()
A.p1 = param
explicitly modifying the A class so that from there, objects which class inherit from A would have the new parameter, lets say:
class C(A):
def __init__(self):
super(C, self).__init__()
print(self.p1)
you can do:
print(A().p1)
B('modified')
C()
and see that even when the first line will print 'static', the last one will print 'modified'.

python class call super's method instead of inherited

I have the following problem
class A:
def __init__(self):
self.window = "<I am window class>"
class B(A):
def __init__(self):
super().__init__()
def createObject(self):
print(self.window) # it works here
self.c = C()
def drawRect(self):
print(self.window) # does not work when called from class C
class C(B):
def __init__(self):
self.draw()
def draw(self):
super().drawRect()
app = B()
app.createObject()
I have figured that, when I call the super().drawRect() function in class C
it sends the <__main__.C object at 0x0000023D99FA6748> as self.
How can I make class C, in function draw() send <__main__.B object at 0x0000017A2BA95358> in the super().drawRect()
I am asking because in the non-simplified code when I call the function from the B class itself, it prints the self.window, but when called from the C class it prints None
how can this be fixed?
what am I doing wrong?
There is no B object when you create an instance of C, there is only a C instance that happens to inherit from B.
So the answer to your question:
How can I make class C, in function draw() send <__main__.B object at 0x0000017A2BA95358> in the super().drawRect()
Is: You can't.
You could create a new B object (but that would defy the purpose of inheritance) alongside your C object.
But it seems more reasonable that you should just call super().__init__ in C.__init__:
class C(B):
def __init__(self):
super().__init__()
self.draw()
def draw(self):
super().drawRect()
It would be weird not to do that anyway. Because inheritance sort of implies that you extend rather than override completely.
But given the fact that you do self.c = C() in your B.createObject method and C inherits from B you should probably figure out another way (e.g. composition) instead of inheritance:
class A:
def __init__(self):
self.window = "<I am window class>"
class B(A):
def __init__(self):
super().__init__()
def createObject(self):
print(self.window) # it works here
self.c = C(self)
def drawRect(self):
print(self.window) # does not work when called from class C
class C(object): # don't inherit from B
def __init__(self, obj):
self.obj = obj
self.draw()
def draw(self):
self.obj.drawRect()
app = B()
app.createObject()
However this will create a reference cycle because you store the C instance as attribute of your B instance and the B instance as attribute of your C instance. It's also possible that you simplified the code too much, maybe the real code prevents composition in some way. However given the current code it seems like inheriting from B is a bad idea for your class C given that it overrides Bs behavior completely and doesn't use any of it's attributes (which it can't anyway because you overrode the methods).
you need to call to
super().__init__()
in class C constructor
Charlie

Only declare variable if doesn't exist yet in the initialization of a class

I'm working for the first time with OOP in a python project. I have 3 classes: PRA, GDB and XLS.
PRA = Main Class
GDB = Responsible for control databases
XLS = Responsible for control xls(x)
UserInput = Responsible for validate user input
How i'm doing:
PRA:
class PRA(GDB, XLS, UserInput):
__init__():
self.gdb_file, self.xls_file = self.ask_questions() # <--- ask_questions is inside the UserInput class.
self.do_something()
XLS:
class XLS:
do_something():
print(self.xls_file)
__init__(self, xls_file):
self.xls_file = xls_file
GDB:
class GDB:
__init__(self, gdb_file):
self.gdb_file = gdb_file
I would like to know if is a bad practice to initialize the gdb_file and xls_file inside the PRA.init, and if not, how can i run the initialization of self.xls_file and self.xls_gdb only in the PRA?
This is not really an answer, but more of an example of code reuse. I'm not sure if this is super Pythonic, though. Anyhow, take a look at this example:
import inspect
class A:
def __init__(self):
self.a = 1
class B:
def __init__(self):
self.b = 2
class C(A, B):
def __init__(self):
for super_class in inspect.getmro(type(self))[1:]:
super_class.__init__(self)
c = C()
print(c.a, c.b)
A bit OT but you may want to think twice about your class names... They are really terrible.
Now wrt/ your question... Technically, the way to handle proper initialisation of a parent class is to call it's initialiser from the child class, ie:
class Parent(object):
def __init__(self, name):
self.name = name
class Child(Parent):
def __init__(self, name, age):
super(Child, self).__init__(name)
self.age = age
You can also explicitly name the parent class, ie:
class Child(Parent):
def __init__(self, name, age):
Parent.__init__(self, name)
self.age = age
but it has a couple drawbacks, like harcoding the parent class in the call (so you have two or more places to edit if you change the parent class), and, more important, not taking care of proper resolution of the "diamond" problem in multiple inheritance (ie D child of B & C, B child of A, C child of A).
Actually, the only reason to not use super() would be when parent classes constructor are not compatible, but this is a huge design smell - if you have two or more parent classes that are not compatible, then you're certainly not using inheritance the right way. And that's actually the case in your example: your "main" class "is" not a "Database controller", it uses a Database controller instead, so you want to use composition, not inheritance
class ServiceA(object):
# code here
class ServiceB(object):
# code here
class Main(object):
def __init__(self, arg1, arg2, arg3):
self.arg1 = arg1
self.service_a = ServiceA(arg2)
self.service_b = ServiceB(arg3)
def run(self):
something = self.service_a.ask(question)
self.service_b.do_something_with(something)
Also you may want to avoid user interactions and costly resource acquisitions in constructors... Better use an entry point method for the first case (like app = MyApp(params); app.run()`) and lazy initialization for the second case (wait until you need the resource to acquire it, use a private attribute as cache an a public property that will take care of acquisition on first access).
[edit]
Someone suggested a dirty hack based on introspection, ie:
import inspect
class A:
def __init__(self):
self.a = 1
class B:
def __init__(self):
self.b = 2
class C(A, B):
def __init__(self):
for super_class in inspect.getmro(type(self))[1:]:
super_class.__init__(self)
c = C()
print(c.a, c.b)
This is definitly not something to do.
If all your parent classes have compatible constructors and properly use super calls, then all you have to do is using super instead:
class A(object):
def __init__(self):
super(A, self).__init__()
self.a = "a"
class B(object):
def __init__(self):
super(B, self).__init__()
self.b = "b"
class C(A, B):
def __init__(self):
super(C, self).__init__()
self.c = "c"
c = C()
print c.a, c.b, c.c
This also takes care of the "diamond inheritance" problem by calling parent classes in the right order:
class D(C, A):
def __init__(self):
super(D, self).__init__()
self.d = "d"
d = D()
print "d : ", d.a, d.b, d.c, d.d
Note that unless only one of the parent classes is a "proper" class (with state) and all other are mixin classes (stateless classes that only add functionalities), multiple inheritance is more often than not a design smell (as well as a maintainance hell). Since Python doesn't use static typing, inheritance is mostly used for implementation inheritance, and for a lot of cases composition/delegation (or just plain composition as in the OP case) is a better solution than implementation inheritance.

Categories