class Parent:
def __init__(self):
self.__num = 100
def show(self):
print("Parent:",self.__num)
class Child(Parent):
def __init__(self):
self.__var = 10
def show(self):
super().show()
print("Child:",self.__var)
obj1 = Child()
obj1.show()
File "main.py", line 12, in show
super().show()
File "main.py", line 6, in show
print("Parent:",self.__num)
AttributeError: 'Child' object has no attribute '_Parent__num'
You need to initialize the parent instance in your child class, because the __num attribute is only set during Parent's initialization, and not during the Child's.
class Child(Parent):
def __init__(self):
super().__init__()
self.__var = 10
def show(self):
super().show()
print("Child:",self.__var)
class Parent:
def __init__(self):
self.__num = 100
def show(self):
print("Parent:",self.__num)
class Child(Parent):
def __init__(self):
super().__init__() # Solution
self.__var = 10
def show(self):
super().show()
print("Child:",self.__var)
obj1 = Child()
obj1.show()
To avoid overridding try this.
class Parent:
def __init__(self):
self.__num = 100
def show(self):
print("Parent:",self.__num)
class Child(Parent):
def __init__(self):
Parent.__init__(self)
self.__var=10
def show1(self):
print("Child:",self.__var)
obj1 = Child()
obj1.show()
Change your Child.init to something like:
def __init__(self):
self.__var = 10
super().__init__()
as the other answers already said, you need to add super().__init__() to the __init__ of your child class.
but also note that there is something called name mangling at work here. read e.g. the 3. Double Leading Underscore: __var on this page.
the short version is: if you wanted to use the attribute self.__num also in the child class you should rename it to self._num (one underscore only).
Related
I have a parent class and a child class. The parent class needs some predefined class variables to run call(). The objects are not defined in the child class.
Question: What is the most pythonic way to pass the variables when calling super() without changing the parent class.
Example:
class Parent:
def __init__(self):
self.my_var = 0
def call(self):
return self.my_var + 1
class Child(Parent):
def __init__(self):
self.different_var = 1
def call(self):
my_var = 0
super().__call__() # What is the most pythonic way of performing this line
I know I could just make my_var in the child class a class object and it would work, but there must be a better. If not that would be an acceptable answer as well.
Your version is just a mixin. You have to __init__ the super.
class Parent:
def __init__(self):
self.my_var = 0
def call(self):
return self.my_var + 1
class Child(Parent):
def __init__(self):
super().__init__() #init super
self.different_var = 1
def call(self):
self.my_var = 50
return super().call() #return call() from super
c = Child()
print(c.call()) #51
Here's an example of what I'm trying to do:
class Parent():
def __init__():
self.parent_var = 'ABCD'
x = Child(self) # self would be passing this parent instance
class Child():
def __init__(<some code to pass parent>):
print(self.parent_var)
foo = Parent()
Now I know what you're thinking, why not just pass parent_var itself to the child instance? Well my actual implementation has over 20 class variables in Parent. I don't want to have to manually pass each variable to the __init__ of the Child instance that's instantiated in Parent-- is there a way to make all Parent class variables available to Child?
EDIT - SOLVED:
This is the way I found that works:
class Parent():
def __init__(self):
self.parent_var = 'ABCD' # but there are 20+ class vars in this class, not just one
x = Child(self) # pass this parent instance to child
class Child():
def __init__(self, parent):
for key, val in vars(parent).items():
setattr(self, key, val)
print(self.parent_var) # successfully prints ABCD
foo = Parent()
If you inherit from the parent class all variables will be present in child classes. Use super init in the child to make sure the parent class instantiates.
class Parent:
def __init__(self):
self.parent_var = 'ABCD'
class Child(Parent):
def __init__(self):
super().__init__()
child = Child()
print(child.parent_var)
prints:
'ABCD'
You would pass the instance of Parent like you would any value.
class Parent:
def __init__(self):
self.parent_var = 'ABCD'
x = Child(self)
class Child:
def __init__(self, obj):
print(obj.parent_var)
Found a solution and wanted to post the answer in case anyone who finds this needs it:
class Parent():
def __init__(self):
self.parent_var = "ABCD" # just an example
x = Child(self) # pass this parent instance (this object) to child
class Child():
def __init__(self, parent):
# copies variables from passed-in object to this object
for key, val in vars(parent).items():
setattr(self, key, val)
print(self.parent_var) # successfully prints ABCD
foo = Parent()
Let's say we have:
class Parent():
def __init__(self):
foo()
def foo(self):
//do stuff
class Child(Parent):
def __init__(self):
Parent.__init__()
class Grandchild(Child):
def __init__(self):
Child.__init__()
def foo(self):
//different stuff
There are a lot of classes at the Child level that use the same foo(). Grandchild level has a slightly different version of foo, but when Grandchild is initiated, the foo() call in Parent.__init__() uses the Parent.foo() instead of Grandchild.foo().
Is there a correct practice when in comes to this kind of situation?
You're not calling the base classes' __init__() methods properly—you need to pass along the self argument to them:
class Parent:
def __init__(self):
self.foo()
def foo(self):
print('Parent stuff')
class Child(Parent):
def __init__(self):
Parent.__init__(self)
class Grandchild(Child):
def __init__(self):
Child.__init__(self)
def foo(self):
print('Grandchild stuff')
if __name__ == '__main__':
gc = Grandchild() # -> Grandchild stuff
If you use super() instead of explicitly stating the base class, you don't have to do that:
class Parent:
def __init__(self):
self.foo()
def foo(self):
print('Parent stuff')
class Child(Parent):
def __init__(self):
# Parent.__init__(self)
super().__init__()
class Grandchild(Child):
def __init__(self):
# Child.__init__(self)
super().__init__()
def foo(self):
print('Grandchild stuff')
if __name__ == '__main__':
gc = Grandchild() # -> Grandchild stuff
Another advantage is that you likely wouldn't have to change the code in a subclass' __init__() method if you changed its base class.
I'm trying something very basic with Python inheritance:
class Parent:
def __init__(self):
self.text = 'parent'
def getText(self):
print self.text
class Child1(Parent):
def __init__(self):
self.x = 'x'
class Child2(Parent):
def __init__(self):
self.x = 'x'
if __name__ == "__main__":
parent = Parent()
child1 = Child1()
child2 = Child2()
parent.getText()
child1.getText()
child2.getText()
but I keep getting
Child1 instance has no attribute 'text'
how are variables passed to children?
You need to call the constructor of the parent classes manually - Here, self.text is initialize in Parent constructor which is never called:
class Child1(Parent):
def __init__ (self):
super(Child1, self).__init__ ()
# or Parent.__init__ (self)
self.x = 'x'
your init function needs to call the parent init
class Child1(Parent):
def __init__(self):
self.x = 'x'
Parent.__init__(self)
In python when you override a function which was supposed to be inherited you override all of it, __init__ is no exception. You should call the functions super method to use the base initializer, or implement the attribute in the constructor you have rewrote.
class Parent:
def __init__(self):
self.text = 'parent'
def getText(self):
print self.text
class Child1(Parent):
def __init__(self):
super(Child1, self).__init__()
self.x = 'x'
child1.getText()
Should work now.
Since python 3.6, we can now use the __init_subclass__ function, which is called automatically before __init__ of the Child.
class Parent:
def __init__(self):
self.text = 'parent'
def __init_subclass__(self):
Parent.__init__(self)
def getText(self):
print(self.text)
class Child1(Parent): pass
class Child2(Parent): pass
classes = [Parent(), Child1(), Child2()]
for item in classes:
item.getText()
output
parent
parent
parent
If you use your Parent class more as a "interface", here is another example.
class Animal():
def __init_subclass__(self, sound):
self.sound = sound
def make_sound(self):
print(self.sound)
class Cat(Animal, sound='meow'): pass
class Dog(Animal, sound='woof'): pass
animals = [Cat(), Dog()]
for animal in animals:
animal.make_sound()
output
meow
woof
I have a class scheme with 2-levels of inheritance. My expectation is that each class constructor would run through- and yet the mid-level class constructor never seems to get hit. What's missing here?
class Base(object):
def __init__(self):
print "BASE"
class Next(Base):
def __init__(self):
super(Base, self).__init__()
print "NEXT"
class Final(Next):
def __init__(self):
super(Next, self).__init__()
print "FINAL"
f = Final()
Outputs:
BASE
FINAL
Why does "NEXT" not print??
You should be calling super() with the current class, not the parent.
class Base(object):
def __init__(self):
super(Base, self).__init__()
print "BASE"
class Next(Base):
def __init__(self):
super(Next, self).__init__()
print "NEXT"
class Final(Next):
def __init__(self):
super(Final, self).__init__()
print "FINAL"
f = Final()
At first glance this might seem redundant ("why can't it just get the class from self?") - but keep in mind that the same self is passed to all three of these __init__ methods when f is created, and that self is always of class Final. Thus, you have to pass super() the class that you want it to find the parent of.
class Base(object):
def __init__(self):
print "BASE"
class Next(Base):
def __init__(self):
super(Next, self).__init__()
print "NEXT"
class Final(Next):
def __init__(self):
super(Final, self).__init__()
print "FINAL"
f = Final()