python class call super's method instead of inherited - python

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

Related

Not being able to inherit the logger [duplicate]

Consider the following code:
class A(object):
def __init__(self):
pass
class B(object):
def __init__(self):
self.something = 'blue'
def get_something(self):
return self.something
class C(A,B):
def __init__(self):
super().__init__()
print(self.get_something())
and then do:
c = C()
which results in something like this:
AttributeError: 'C' object has no attribute 'something'
I suppose this happens due to the constructor of B not being called when using super(). Is there a way to achieve the correct behavior with Python 3?
Superclasses should use super if their subclasses do. If you add the super().__init__() line into A and B your example should work again.
Check the method resolution order of C:
>>> C.mro()
[__main__.C, __main__.A, __main__.B, builtins.object]
This article should clear things up.
As others have mentioned, the method resolution order is key here. If you want to call multiple superclass constructors, then you will have to call them directly.
class A(object):
def __init__(self):
pass
class B(object):
def __init__(self):
self.something = 'blue'
def get_something(self):
return self.something
class C(A,B):
def __init__(self):
A.__init__(self)
B.__init__(self)
print(self.get_something())

How does multiple inheritance work with super?

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__()

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

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 multiple inheritance constructor not called when using super()

Consider the following code:
class A(object):
def __init__(self):
pass
class B(object):
def __init__(self):
self.something = 'blue'
def get_something(self):
return self.something
class C(A,B):
def __init__(self):
super().__init__()
print(self.get_something())
and then do:
c = C()
which results in something like this:
AttributeError: 'C' object has no attribute 'something'
I suppose this happens due to the constructor of B not being called when using super(). Is there a way to achieve the correct behavior with Python 3?
Superclasses should use super if their subclasses do. If you add the super().__init__() line into A and B your example should work again.
Check the method resolution order of C:
>>> C.mro()
[__main__.C, __main__.A, __main__.B, builtins.object]
This article should clear things up.
As others have mentioned, the method resolution order is key here. If you want to call multiple superclass constructors, then you will have to call them directly.
class A(object):
def __init__(self):
pass
class B(object):
def __init__(self):
self.something = 'blue'
def get_something(self):
return self.something
class C(A,B):
def __init__(self):
A.__init__(self)
B.__init__(self)
print(self.get_something())

Categories