Inheritance


#1

We define the Car class as such:

class Car(object):
  condition = "new"
  def __init__(self, model, color, mpg):
    self.model = model
    self.color = color
    self.mpg   = mpg

In creating the child class ElectricCar, which is supposed to inherit member variables from the parent Car class, why must we rewrite self.model, self.color and self.mpg?

class ElectricCar(Car):
  def __init__(self, model, color, mpg, battery_type):
    self.model = model
    self.color = color
    self.mpg = mpg
    self.battery_type = battery_type

#2

Because the __init__ method of the super class is not called so those member variables must be declared in the derived class.

The member variables are not inherited, but there are references to them in methods of the super class that the derived class instances inherit.


Another thought to test out… The super class __init__ method is called, and member variables are inherited, but not initialized, so their values are, None. The derived class __init__ method overrides that.


#3

I’m still a little confused.

class ElectricCar(Car):
  pass

my_car = ElectricCar("Hovercar","black",33)
print my_car.model

If I leave the ElectricCar class empty, create an instance of ElectricCar and ask it to print the model, it correctly prints “Hovercar”. Is this because, as you said, the derived class inherits the methods from the super class, which reference those variables? However, if I want to add a variable to the derived class, I have to reinitialize all variables within the derived class __init__?


#4

I’d be interested to know how that code behaves after a page refresh. Care to try it out?

A little testing opened my eyes…

>>> class Car(object):
	def __init__(self, model, color, mpg):
		self.model = model
		self.color = color
		self.mpg = mpg
	def display_car(self):
            print ("This is a {} {} with {:d} mpg".format(self.color, self.model, self.mpg))

>>> class ElectricCar(Car):
	pass

>>> my_car = ElectricCar("Hovercar","black",33)
>>> my_car.display_car()
This is a black Hovercar with 33 mpg
>>> 

Now to contemplate…


#5

Sure thing. I just refreshed, and it still prints correctly. Here is the full code:

class Car(object):
  condition = "new"
  def __init__(self, model, color, mpg):
    self.model = model
    self.color = color
    self.mpg   = mpg
  
  def display_car(self):
    return "This is a " + my_car.color + " " + my_car.model + " with " + str(my_car.mpg) + " MPG."
  
  def drive_car(self):
    self.condition = "used"

class ElectricCar(Car):
  pass

my_car = ElectricCar("Hovercar","black",33)
print my_car.model

Console shows “Hovercar”


#6

Please see my edited post above yours. Giving this some thought and will dig around for under the hood articles that describe this in detail. Could be a while…

Can be rewritten for Python 2+,

"This is a %s %s with %d MPG." % (self.color, self.model, self.mpg)

Note the instance context, self.


#7

Thank you for the help. I should’ve known about the %s placeholder, but what differentiates %d? Is that used for integers?


#8

Yes. digits. Will have to root out the docs on what differentiates d from i, as in %i.


Turns out %d is a dummy inherited from C, and kept to support it. d and i are the same in Python, but not in C.


Here is a great reference for both old and new formatting.

https://pyformat.info/


Aside

Some more testing (Python 3 using old format)

>>> print ("% %" % (6, 7))
Traceback (most recent call last):
  File "<pyshell#16>", line 1, in <module>
    print ("% %" % (6, 7))
TypeError: not all arguments converted during string formatting
>>> print ("% %" % ('6', '7'))
Traceback (most recent call last):
  File "<pyshell#17>", line 1, in <module>
    print ("% %" % ('6', '7'))
TypeError: not all arguments converted during string formatting
>>> print ("%s %s" % (6, 7))
6 7
>>> print ("%d %d" % ('6', '7'))
Traceback (most recent call last):
  File "<pyshell#19>", line 1, in <module>
    print ("%d %d" % ('6', '7'))
TypeError: %d format: a number is required, not str
>>> print ("%d %d" % (6, 7))
6 7
>>>

#9

Digging around under the hood…

We have the Car class from above. Let’s try to add battery_type to the ElectricCar by writing an __init__ method…

>>> class ElectricCar(Car):
	def __init__(self, battery_type):
		self.battery_type = battery_type

		
>>> my_car = ElectricCar("Molten Salt","Hovercar","black",33)
Traceback (most recent call last):
  File "<pyshell#29>", line 1, in <module>
    my_car = ElectricCar("Molten Salt","Hovercar","black",33)
TypeError: __init__() takes 2 positional arguments but 5 were given
>>> 

That throws a wrench in the gearworks. We have no option now but to override the inherited __init__ variables, otherwise we have no way to pass them up in the background (which we observed earlier).

>>> class ElectricCar(Car):
	def set_battery_type(self, battery_type):
		self.battery_type = battery_type
	pass

>>> my_car = ElectricCar("Hovercar","black",33)
>>> my_car.set_battery_type("Molten Salt")
>>> my_car.battery_type
'Molten Salt'
>>> my_car.display_car()
This is a black Hovercar with 33 mpg
>>> 

The subject of override comes up in the next few lessons so I’m going to stop right here. We can carry this example forward when you cross that bridge.

Sneak Peak

Intro to override and inherit that permits all the arguments…

>>> class ElectricCar(Car):
	def __init__(self, model, color, mpg, battery_type):
		Car.__init__(self, model, color, mpg)
		self.battery_type = battery_type

		
>>> my_car = ElectricCar("Hovercar","black",33,"Molten Salt")
>>> 
>>> my_car.battery_type
'Molten Salt'
>>> my_car.display_car()
This is a black Hovercar with 33 mpg
>>> 

#10

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.