SmithLogo

CSC 111

Introduction to Computer Science Through Programming

Smith Computer Science



Lecture Notes 25: Inheritance, Polymorphism, and Importing





Recap: Inheritance and Polymorphism

Last class, we had the following program (That didn't work!):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
class Fruit_Buddy:

  def __init__(self, id, name, fav_fruit, buddy_list):
    self.id = id
    self.name = name
    self.fav_fruit = fav_fruit
    self.buddies = buddy_list

  def add_buddy(self,other):
    # adding the other's id (this can be made better!)
    self.buddies.append(other) 

  def add_buddy_list(self,buddy_list):
    for bud in buddy_list:
      self.add_buddy(bud)   

  def print_buddies(self):
    print("Buddies of {} :".format(self.name), end = " ")
    for bud in self.buddies:
      print(bud.name, end=" ")
    if len(self.buddies) == 1:
      if self.buddies[0] == self.id:
        print ("** Sad Trombone **")
    print()

  def remove_buddy(self,other):
    self.buddies.remove(other)
    self.remove_as_buddy(other)

  def remove_as_buddy(self,other):
    other.remove_buddy(self)          

# constructing social network FB: Fruit Buddies
def main():

  # the constructor can be improved with default vals
  FB_user_01 = Fruit_Buddy(101, "Alice", "Apples",[])
  FB_user_02 = Fruit_Buddy(102, "Bob", "Bananas",[])
  FB_user_03 = Fruit_Buddy(103, "Cathy", "Clementines",[])
  FB_user_04 = Fruit_Buddy(104, "Dan", "Dates",[])

  # first way to add buddies
  FB_user_01.add_buddy_list([FB_user_01, FB_user_02, FB_user_03])
  FB_user_02.add_buddy_list([FB_user_01, FB_user_02, FB_user_03])
  FB_user_03.add_buddy_list([FB_user_02, FB_user_03])
  FB_user_04.add_buddy(FB_user_04)

  FB_user_01.print_buddies()
  FB_user_02.print_buddies()
  FB_user_03.print_buddies()
  FB_user_04.print_buddies() 

  FB_user_01.remove_buddy(FB_user_02)
  FB_user_01.print_buddies()
  FB_user_02.print_buddies()

if __name__ == "__main__":
  main()


Activity 1 [2 minutes]:
Why is this failing?
(Wait; then Click)

The reason this fails is that "If the element one wants to remove from a list doesn't exist, Python throws an error:
"ValueError: list.remove(x): x not in list exception."

That is why, we might want to first make sure the element exists before removing it!


Activity 2 [2 minutes]:
Would the following modification help?

1
2
3
  def remove_buddy(self,other):
    self.remove_as_buddy(other)
    self.buddies.remove(other)


(Wait; then Click)

NOPE!

Because we go into an Infinite Loop!




Activity 3 [2 minutes]:
What is another thing we could do?
(Wait; then Click)

1
2
3
4
5
6
7
8
  def remove_buddy(self,other):
    for bud in self.buddies:
      if bud == other:
        self.buddies.remove(bud)
        self.remove_as_buddy(other)

  def remove_as_buddy(self,other):
    other.remove_buddy(self)


The in operator can be used outside a loop too!

The follwing is a valid way to avoid the error "ValueError: list.remove(x): x not in list":

  def remove_buddy(self,other):
    if other in self.buddies:
      self.buddies.remove(other)
      self.remove_as_buddy(other)

  def remove_as_buddy(self,other):
    other.remove_buddy(self) 




Inheritance

We'll reproduce this example: With Dogs!

Activity 4 [4 minutes]:

Together, we'll perform the following actions:

  • Open the project Dog Park in Replit
  • Run the base code
  • What does __str__ do?
  • Now, we'll modify the definition of the class Dog and add three Child classes
  • In main, print "type(miles)"
  • In main, print "isinstance(miles, Dog)"
  • In main, print "isinstance(miles, Bulldog)"
  • Extend the Dog class functionality by adding to the JackRussellTerrier class
  • Usethe parent class inside the child class by using




Polymorphism

We've used polymorphism before (multiple uses of the same operation... like "+")

However, we can have methods with the same name under different classes!
Check this out.

As an example, check out the method Speak in the Dog Park example above




Using super() on construction

Please check out this article: https://realpython.com/python-super/

The idea is that, when we want to initialize an object under a sub-class, we can use the parent's constructor (__init__) and maybe adding some other details to the cub-class extended attributes.

Let's see the example for a Rectangle vs a Square


Go to Replit Exercise Rectangular

Activity 5 [2 minutes]:
  1. First, see the use of the two distinct classes Rectangle and Square
  2. Now follow the recommended changes and discuss




One last example (To analyze at home)

(Wait; then Click)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
class Character:
  def __init__(self, name="NA", player="NPC", health = 10, focus = 10):
    self.name = name
    self.player = player
    self.health = health
    self.focus = focus

  def print_char(self):
    print("{}, played by {}, has {} health and {} focus left".format(self.name, self.player, self.health, self.focus))

  def apply_damage(self,damage):
    if self.health <= damage:
      self.health = 0
      print ("AArrghhh! ... ** {} has died **".format(self.name))
    else:
      self.health -= damage  

  def apply_charm(self,charm):
    if self.focus <= charm:
      self.focus = 0
      print ("Oooohhh! ... ** {} has been charmed **".format(self.name))
    else:
      self.focus -= charm

class Warrior(Character):
  def __init__(self,name="NA", player="NPC", health = 10, focus = 10, weapon = "punch", damage = 1):
    Character.__init__(self,name, player, health, focus)
    self.weapon = weapon
    self.damage = damage

  def set_weapon (self, weapon = "punch", damage = 1):
    self.weapon = weapon
    self.damage = damage

  def  use_weapon(self,other):
    other.apply_damage(self.damage)


class Bard(Character):
  def __init__(self,name="NA", player="NPC", health = 10, focus = 10, instrument = "voice", charm = 1):
    Character.__init__(self,name, player, health, focus)
    self.instrument = instrument
    self.charm = charm

  def set_instrument (self, instrument = "voice", charm = 1):
    self.instrument = instrument
    self.charm = charm  

  def  use_instrument(self,other):
    other.apply_charm(self.charm)


def main():
  c1 = Warrior("Orkhina", "Mariana", 20, 5)
  c1.set_weapon("Axe",5)
  c2 = Bard("Elfrank", "Pablo", 6, 20)
  c2.set_instrument("Flute", 10)

  c1.use_weapon(c2)
  c2.print_char()

  c2.use_instrument(c1)
  c1.print_char()

if __name__ == "__main__":
  main()








Additional readings

Check out this article on Inheritance

Also this one with a Polymorphism




Final Project DIscussion!

Check the Moodle FInal Project steps!




Homework

[Due for everyone]
Homework 07 Due on Monday



[Optional]