Chapter 12 Classes

12.8 Sub Classes

In Python, the type hierarchy is achieved by sub classes. A sub class is an extention to a base class, which represents a specific sub type of the base class in a type hierarchy. For example, the vehicle type can be represented by a sub class of a car type.

As mentioned earlier, sub classes can make Python programs simpler by inheriting attributes and methods of the base class, so that code duplication is minimized. To give an example, consider a CreditAccount class, which represents a specific type of accounts that has a credit limit. Overdraft of funds is allowed within the credit limit, resulting in a negative balance. In all the other aspects, a credit account behaves exactly the same as the base Account class.

The CreditAccount class can be developed in two steps. First, an empty extention of the base class Account can be made, which inherits all the attributes and methods of the Account class. Second, the credit limit feature can be added by overriding some existing functionalities.

The syntax for defining a sub class of an existing class is shown in Fig. 12.2. It is an extension to the class statement, with the only difference being an additional base class name after the class name, which is enclosed in a pair of parentheses. Using this statement, the base class attributes and methods are inherited automatically in the sub class. The statement block under the class line defines the attributes and methods that are unique in the sub class.


Fig. 12.2 The class statement with class extension

Fig. 12.2 The class statement with class extension

Save the following code at c:\myPython folder as account.py.:

class Account:
    total =100001
    def __init__(self, name, balance=0):
        self.name=name
        self.balance=balance
        self.number=Account.total
        Account.total+=1

    def deposit(self , amount):
        if amount <=0:
            print ('Error , negative amount.')
            return
        self.balance+=amount

    def withdraw(self, amount):
        if amount <0:
            print ('Error: negative amount')
            return
        if amount >self.balance:
            print ('Error: insufficient fund')
            return
        self.balance -=amount

    def __str__(self):
        return '''Account holder name: %s
            Account number: %d
            Balance: %.2f ''' %(self.name, self.number, self. balance)


    def __str__(self):
        resultStr='Account holder name: ' + self.name + ', Account number: ' + \
        str(self.number) + ', Balance: ' + str(self.balance) + '.'
        return resultStr

To make an empty extension to the class Account, a single pass statement can be used as the statement block of the new class. At c:\myPython folder, starts IDLE (or python) and do the followings:

>>> from account import Account
>>> class CreditAccount(Account):
...     pass
...
>>> a1=Account('John')
>>> a2=CreditAccount('Marina')
>>> print (a1)
Account holder name: Jon, Account number: 100001, Balance: 0.
>>> print (a2)
Account holder name: Marina, Account number: 100002, Balance: 0.
>>> a2.deposit(100)
>>> print(a2)
Account holder name: Marina, Account number: 100002, Balance: 100.
>>> Account.deposit(a2, 100) # a2 is Account
>>> print(a2)
Account holder name: Marina, Account number: 100002, Balance: 200.
>>> CreditAccount.deposit(a2, 100) # a2 is CreditAccount
>>> print(a2)
Account holder name: Marina, Account number: 100002, Balance: 300.
>>> CreditAccount.deposit(a1, 100) # a1 is CreditAccont
>>> print (a1)
Account holder name: Jon, Account number: 100001, Balance: 100.

In the example above, the class CreditAccount is defined as empty extension to the class Account. Several instances of Account and CreditAccount are constructed. It can be seen that the CreditAccount instance a2 has all the attributes and methods of Account instances, and behaves almost identically compared with the Account instances a1. For example, the constructor of a2 is the same as that of a1, taking a name argument. The methods __str__ and deposit also apply to a2. Interestingly, a2 also participates in the automatic allocation of account numbers.

The reason behind these observations is that, when a CreditAccount method instance is called, but the method is not defined in the CreditAccount class, the corresponding method in Account is called. For example, when a2.deposit(100) is called, Python tries to call CreditAccount.deposit(a2, 100). However, deposit is not defined in CreditAccount. As a result, Python calls the base class method Account.deposit(a2, 100). The same happens for __init__ and __str__. When a2=CreditAccount(‘Marina’) is executed, Account.__init__(a2, ‘Marina’) is called.

As mentioned earlier, Python performs type checking when the self argument is assigned. Apparently, the CreditAccount instance a2 passes the type checking of Account methods, which implies that a2 is also an Account instance. This is intuitively understandable: a car instance is also a vehicle instance.

© Copyright 2024 GS Ng.

Next Section - 12.9 Overriding Methods