Customizing functionality via inheritance

1. Customizing functionality via inheritance

Great job so far! In the previous video, you learned that inheritance allows us to encode is-a relationships between classes.

2. Hierarchy

For example, a SavingsAccount is a special kind of BankAccount that has all the bankaccount functionality, but also contains additional properties like an interest rate and a method to compute interest.

3. What we have so far

Here's where we stopped in the last video. We could already create SavingsAccount objects, but they did not have any functionality that bank account did not have. Let's start customization by

4. Customizing constructors

adding a constructor specifically for SavingsAccount. It will take a balance parameter, just like BankAccount, and an additional interest_rate parameter. In that constructor, we first run the code for creating a generic bankaccount by explicitly calling the init method of the bankAccount class. Notice that we use BankAccount-dot-init to tell Python to call the constructor from the parent class, and we also pass self to that constructor. Self in this case is a SavingsAccount -- that's the class we're in -- but remember that in Python, instances of a subclass are also instances of the parent class. so it is a BankAccount as well, and we can pass it to the init method of BankAccount. Then we can add more functionality, in this case just initializing an attribute. You actually aren't required to call the parent constructor in the subclass, or to call it first -- you can use new code entirely -- but you'll likely to almost always use the parent constructor.

5. Create objects with a customized constructor

Now when we create an instance of the SavingsAccount class, the new constructor will be called, and in particular, the interest_rate attribute will be initialized.

6. Adding functionality

In the exercises, you saw you can add methods to a subclass just like to any other class. In those methods you can use data from both the child and the parent class. For example here, we add a compute_interest method that returns the amount of interest in the account.. Don't worry about the exact formula, just notice that we multiply the balance attribute - which was inherited from the BankAccount parent - by an expression involving the interest_rate attribute that exists only in the child SavingsAccount class.

7. Customizing functionality

Now let's talk about customizing functionality. SavingsAccount inherits the withdraw method from the parent BankAccount class. Calling withdraw on a savings instance will execute exactly the same code as calling it on a generic bank account instance. We want to create a CheckingAccount class, which should have a slightly modified version of the withdraw method: it will have a parameter and adjust the withdrawal amount.

8. Customizing functionality

Here's an outline of what that could look like. Start by inheriting from the parent class, add a customized constructor that also executes the parent code, a new deposit method, and a withdraw method, but we add a new argument to withdraw - fee, that specifies the additional withdrawal fee. We compare the fee to some fee limit, and then call the parent withdraw method, passing a new amount to it -- with fees subtracted. So this method runs almost the same code as the BankAccount's withdraw method without re-implementing it - just augmenting. Notice that we can change the signature of the method in the subclass by adding a parameter, and we again, just like in the constructor, call the parent version of the method directly by using parent-class-dot syntax and passing self.

9. Comparison

Now when you call withdraw from an object that is a CheckingAccount instance, the new customized version will be used, but when you call it from regular BankAccount, the basic version will be used. The interface of the call is the same, and the actual method that is called is determined by the instance class. This is an application of polymorphism, and we'll learn more about it later in the course. Another difference is that for a CheckingAccount instance, we could call the method with 2 parameters. But trying this call for a generic BankAccount instance would cause an error, because the method defined in the BankAccount class was not affected by the changes in the subclass.

10. Let's practice!

Now, let's go customize some methods!