Polymorphism and Duck Typing

Polymorphism and Duck Typing#

In the context of OOP, polymorphism is when we treat objects of different classes as the same class. There are a few different kinds of polymorphism, however the most common you will see in this course is “Subtyping polymorphism”.

We are taking this opportunity to introduce key terms in Python OOP as you will need to know it to be considered a well rounded object-oriented programmer.

Subtyping Polymorphism#

Subtyping polymorphism is when a child class overrides a method of a parent class. This should not be a new idea to you, as we have been using this idea from the start of this lesson!

Example

In the example below, a list of accounts is created with different account types. We can call withdraw() on any instance.

class BaseAccount:

    def __init__(self, balance, holder_name):
        self.balance = balance
        self.holder_name = holder_name

    def withdraw(self, amount):
        self.balance -= amount

class FeeChargingAccount(BaseAccount):

    def withdraw(self, amount):
        fee = 2.50
        super().withdraw(amount + fee) # Calls the method defined on line 8

accounts = [
    BaseAccount(1000, "Bruce Wayne"),
    Account(100, "Clark Kent")
]

for account in accounts:
    account.withdraw(50)

Dynamic Polymorphism and Duck Typing#

The Python interpreter only checks for a method with a matching name at execution time. This is known as dynamic polymorphism, which is in contrast to static polymorphism where compatibility is checked at compile time. Dynamic polymorphism describes the implementation of polymorphism in a programming language and is not mutually exclusive with subtyping polymorphism.

Dynamic polymorphism enables the idea of polymorphism to extend to objects that are not part of a shared class hierarchy.

For example below the classes below are independent, however they both implement a withdraw() method with different behaviour.

class SavingsAccount:

    def __init__(self, balance):
        self.balance = balance

    def withdraw(self, amount):
        self.balance -= amount

class FeeChargingAccount:

    def __init__(self, balance):
        self.balance = balance

    def withdraw(self, amount):
        fee = 2.50
        super().withdraw(amount + fee) # Calls the method defined on line 8

accounts = [
    SavingsAccount(1000),
    FeeChargingAccount(100)
]

for account in accounts:
    account.withdraw(50)

In Python, this idea is often called duck typing, which refers to the idea of “if it quacks like a duck, it is a duck”. Instead of enforcing strict types as in other languages, Python encourages programmers to call methods on objects and let the object respond or throw an exception if the method is not implemented.

Code Challenge: Identify Yourself!

The boffins have now upgraded your ship with a communicator so that you can talk to other ships in the galaxy.

../../_images/identify_yourself.png

Complete the identify_ship function so that it calls each ship instance’s identify method and returns the result.

Requirements

  • name: identify_ship

  • parameters: ship an instance of one of Spaceship, AttackShip, InfiniteCargoShip

  • returns: the value of the ship’s identify method.

Example

>>> identify_ship(AttackShip())
ATTACK SHIP

Here is some code for you to start with:

class Spaceship:
    def identify(self):
        return "BASIC SPACESHIP"

class AttackShip:
    def identify(self):
        return "ATTACK SHIP"

class InfiniteCargoShip:
    def identify(self):
        return "CARGO SHIP"

def identify_ship(ship):
    # COMPLETE ME


print(identify_ship(AttackShip()))
Solution

Solution is locked

Code Challenge: Upgraded Lasers

Note

For this exercise you’ll need the solution to Super > “Attack Ship”

The AttackShip class is receiving an advanced upgrade to its lasers. This means the laser is now more efficient and uses half as much fuel each time it is fired.

../../_images/attack_ship2.png

Add an AttackShip class which uses half as much fuel as Spaceship each time the lasers are fired.

AttackShip Specifications

  • Inherit from the provided Spaceship class

  • Additional attributes:

    • rounds the number of rounds remaining on the ship

  • __init__ method with

    • Parameters:

      • self

      • name

      • fuel_capacity

      • rounds

    • Actions:

      • Call the Spaceship constructor to set common attributes with the same name

      • Set the rounds attribute

  • fire_round method with

    • Parameters:

      • self

    • Actions:

      • Reduces the remaining rounds by 1.

    • Returns:

      • None (no return statement required)

  • fire_laser method with

    • Parameters:

      • self

    • Actions:

      • Subtract 5 units from fuel weight on board

    • Returns:

      • None (no return statement required)

Here is some code for you to start with:

class Spaceship:
    def __init__(self, name, fuel_capacity):
        self.name = name
        self.fuel_weight = 0
        self.fuel_capacity = fuel_capacity

    def refuel(self, weight):
        if self.fuel_weight + weight > self.fuel_capacity:
            self.fuel_weight = self.fuel_capacity
        else:
            self.fuel_weight += weight

    def fire_laser(self):
        if self.fuel_weight >= 10:
            self.fuel_weight -= 10



# X-Wings carry 6 torpedos
xwing = AttackShip("X Wing", 500, 6)
xwing.refuel(500)  # Fill 'er up!

xwing.fire_laser()
print(xwing.fuel_weight)
Solution

Solution is locked