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
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()
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).
class Car(object):
condition = "new"
def __init__(self):
self.model =raw_input()
self.color = raw_input()
self.mpg = raw_input()
class Jeep(object):
condition = "new"
def __init__(self):
self.model =raw_input()
self.color = raw_input()
self.mpg = raw_input()
class ElectricCar(Car, Jeep):
def __init__(self, battery_type):
self.battery_type=battery_type
super(ElectricCar, self).__init__()
def printer(self):
print self.model
car = ElectricCar('battery')
car.printer()
print car.__dict__
Only 3 times I am getting to enter my entries instead of 6 plus I am confused how to avoid ambiguity as model is in car as well as jeep. How do I tackle them?
There are two problems here:
Python objects have one dict for storing attributes, there can be no attribute hiding like in other programming languages
You are incorrectly using multiple inheritance and super.
Python will not work how you want it to work. Each instance in Python has one shared dict which all classes share for attribute access. This means you cannot hide an attribute, only overwrite it. To do what you want you must a unique name for model in Car and a unique name in Jeep. eg. car_model and jeep_model. A quick test that shows this:
class Parent(object):
def __init__(self):
super(Parent, self).__init__()
self.value = "parent"
def parent_value(self):
return self.value
class Child(Parent):
def __init__(self):
super(Child, self).__init__()
self.value = "child"
def child_value(self):
return self.value
p = Parent()
assert p.parent_value() == "parent"
c = Child()
assert c.child_value() == "child"
assert c.parent_value() == "child"
You could use "private" variables. These aren't really private, python just does something called name mangling to make them harder to accidentally overwrite.
class Parent(object):
def __init__(self):
super(Parent, self).__init__()
self.__value = "parent"
def parent_value(self):
return self.__value
class Child(Parent):
def __init__(self):
super(Child, self).__init__()
self.__value = "child"
def child_value(self):
return self.__value
p = Parent()
assert p.parent_value() == "parent"
c = Child()
assert c.child_value() == "child"
assert c.parent_value() == "parent"
print(vars(c))
# prints {'_Child__value': 'child', '_Parent__value': 'parent'}
c._Parent__value = "new value"
assert c.parent_value() == "new value"
Finally, your __init__ methods in your base classes do not make a call to super. In non-multiple inheritance this is not necessary for the base class. However, in your case (which uses multiple inheritance) the set of __init__ methods will get to Car.__init__ and then stop without calling Jeep.__init__.
class Car(object):
def __init__(self):
print("init car")
super(Car, self).__init__()
print("assigning car")
self.car = "car"
class Jeep(object):
def __init__(self):
print("init jeep")
super(Jeep, self).__init__()
print("assigning jeep")
self.jeep = "jeep"
class ElectricCar(Car, Jeep):
def __init__(self):
print("init electric car")
super(ElectricCar, self).__init__()
print("assigning electric")
self.electric = "electric"
car = ElectricCar()
# prints the following:
# init electric car
# init car
# init jeep
# assigning jeep
# assigning car
# assigning electric
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()