## ![](https://s3.amazonaws.com/python-ga/images/GA_Cog_Medium_White_RGB.png) {.separator}

Python Programming: Class Inheritance

--- ## Learning Objectives *After this lesson, you will be able to…* - Implement inheritance. - Describe what has been inherited from one class to another. - Overwrite variables and methods. --- ## Discussion: Similar Classes `Phone` is a class — there are hundreds of types of phones. - What attributes and functions would a `Phone` have? What about an `iPhone`? Or `android_phone`? - `iPhone` and `android_phone` would be objects of the `Phone` class. - But, there are different types of iPhones and Android phones. - Should `IPhone` and `AndroidPhone` be classes themselves? What would you do? --- ## Introduction: Inheritance `AndroidPhone` and `IPhone` are separate classes *and* in the `Phone` class. This is called **inheritance**: making classes that are subsets of other classes. `Phone` is the **parent** class. It's a regular class! All phones: - Have a phone number. - Can place phone calls. - Can send text messages. `IPhone` is a **child** class. The child class **inherits** methods and properties from the parent class but can also define its own functionality. iPhones uniquely: - Have an `unlock` method that accepts a fingerprint. - Have a `set_fingerprint` method that accepts a fingerprint. --- ## We Do: Inheritance All phones have a phone number, can place phone calls, and can send text messages. Start a new file, `Phone.py`. In it, let's start and test the class: ```python class Phone: def __init__(self, phone_number): self.number = phone_number def call(self, other_number): print("Calling from", self.number, "to", other_number) def text(self, other_number, msg): print("Sending text from", self.number, "to", other_number) print(msg) test_phone = Phone(5214) test_phone.call(515) test_phone.text(51121, "Hi!") ``` --- ## We Do: `IPhone` Class Underneath the `Phone` class definition, let's create the `IPhone` class. ```python class IPhone(Phone): # Class definitions accept a parameter specifying what class they inherit from. def __init__(self, phone_number): # super()` calls the `init` defined in the parent class. super().__init__(phone_number) # Now self.number is set, because that's what happens in the parent Phone _init_. ``` --- ## We Do: `IPhone` Class iPhones uniquely: - Have an `unlock` method that accepts a fingerprint. - Have a `set_fingerprint` method that accepts a fingerprint. ```python class IPhone(Phone): def __init__(self, phone_number): super().__init__(phone_number) # Under the call to super, we can define unique IPhone variables. # Regular Phone objects won't have this! self.fingerprint = None # Here are methods unique to IPhone objects: def set_fingerprint(self, fingerprint): self.fingerprint = fingerprint def unlock(self, fingerprint=None): if (fingerprint == self.fingerprint): print("Phone unlocked. Fingerprint matches.") else: print("Phone locked. Fingerprint doesn't match.") ``` --- ## Side Discussion: Edge Cases Look at: ```python def unlock(self, fingerprint=None): if (fingerprint == self.fingerprint): print("Phone unlocked. Fingerprint matches.") else: print("Phone locked. Fingerprint doesn't match.") ``` What if `self.fingerprint` is currently `None`? We need to account for this! ```python def unlock(self, fingerprint=None): if (self.fingerprint == None): print("Phone unlocked. No fingerprint needed.") elif (fingerprint == self.fingerprint): print("Phone unlocked. Fingerprint matches.") else: print("Phone locked. Fingerprint doesn't match.") ``` When programming, always watch for **edge cases**. This isn't specific to classes! --- ## We Do: Testing `IPhone` Add some test lines at the bottom: ```python my_iphone = IPhone(151) my_iphone.unlock() my_iphone.set_fingerprint("Jory's Fingerprint") my_iphone.unlock() my_iphone.unlock("Jory's Fingerprint") # And we can call the Phone methods: my_iphone.call(515) my_iphone.text(51121, "Hi!") ``` Try it! Then, try this. Why does it fail? ```python # Let's try a Phone object on an iPhone method. test_phone.unlock() ``` --- ## Quick Recap: Inheritance - A class can inherit from another class — a parent class and a child class. - The child class can declare its own variables and methods, but it also has access to all the parents'. ```python ## Parent class: A regular class ## class Phone: def __init__(self, phone_number): self.number = phone_number def call(self, other_number): print("Calling from", self.number, "to", other_number) test_phone = Phone(5214) # It's a regular class! test_phone.call(515) ## Child class: Pass in the parent class and call super() ## class IPhone(Phone): def __init__(self, phone_number): super().__init__(phone_number) # Under the call to super, define unique child class variables and methods. # Parent class objects won't have this! self.fingerprint = None def set_fingerprint(self, fingerprint): self.fingerprint = fingerprint my_iphone = IPhone(151) # Create an object as usual. my_iphone.set_fingerprint("Jory's Fingerprint") # Call a method. my_iphone.call(515) # Call a method from the parent class. ``` --- ## I Do: Overwriting Attributes **Next up: Overwriting attributes!** Let's switch to a new example. You don't need to follow along. Here's a regular `Building` class: ```python class Building(object): # Class variables avg_sqft = 12500 avg_bedrooms = 3 # No __init__ - there are no instance variables to declare! # This is possible in any class, not just inheritance. (Building is a normal class.) def describe_building(self): print('Avg. Beds:', self.avg_bedrooms) print('Avg. Sq. Ft.:', self.avg_sqft) def get_avg_price(self): price = self.avg_sqft * 5 + self.avg_bedrooms * 15000 return price my_building = Building() my_building.describe_building() ``` --- ## I Do: Inheriting Building Inheriting from `Building`, we can create a `Mansion` class. ```python # Call in the parent, Building, to the class definition. class Mansion(Building): # Our child class definition goes here. # Will have the same class variables, instance variables, and methods as Mansion objects. ``` --- ## Overwriting Variables What if we want the class variables to have different values? We can set new ones. Remember, child classes do not affect the parent class. ```python class Mansion(Building): # Overwrite the class variables. avg_sqft = 6 avg_bedrooms = 1 # We don't have a call to super __init__. Why? # There's no __init__ in the parent to call! ### Now, let's try it out. ### # This still has the old values. my_building = Building() my_building.describe_building() # The mansion object has the new class variables! avg_mansion = Mansion() avg_mansion.describe_building() ``` --- ## Discussion: Child Class Methods In the `Building` class, we have: ```python def get_avg_price(self): price = self.avg_sqft * 5 + self.avg_bedrooms * 15000 return price ``` What if a `Mansion`'s price calculation is different? What do you think we can do? --- ## Overwriting Methods We know that we can overwrite variables. Turns out, we can also overwrite methods! ```python class Mansion(Building): def get_avg_price(self): return 1000000 mans = Mansion() bldg = Building() bldg.get_avg_price() # # returns `self.avg_sqft * 5 + self.avg_bedrooms * 15000` mans.get_avg_price() # Returns 1000000 ``` --- ## Quick Review When we make child classes, we can overwrite class variables and methods. ```python class Building(object): # Class variables avg_sqft = 12500 avg_bedrooms = 3 def get_avg_price(self): price = self.avg_sqft * 5 + self.avg_bedrooms * 15000 return price class Mansion(Building): # Overwrite the class variables. avg_sqft = 6 avg_bedrooms = 1 def get_avg_price(self): return 1000000 ``` --- ## Knowledge Check Consider the following classes: ```python class Animal(object): def is_mammal(self): return True def is_alive(self): return True class Grasshopper(Animal): def is_small(self): return True ``` You instantiate two objects: `bug = Grasshopper()` and `cat = Animal()`. Which of the following instance methods are available for each? 1. `bug.is_mammal()` 2. `bug.is_alive()` 3. `bug.is_small()` 4. `bug.is_animal()` --- ## Summary and Q&A Inheritance: - Allows us to make classes using other classes as templates. - Has a **parent** class (`Phone`) and a **child** class (`IPhone`). - The parent class is still a usable class! Child classes: - `inherit` methods and properties from a parent class. - Have access to all of the functionality of its parent. - Can have new attributes and methods. - They won't be available to the parent. - Can overwrite values from the parent class.