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.
Complete the identify_ship function so that it calls each ship instance’s identify method and returns the result.
Requirements
name:
identify_shipparameters:
shipan instance of one ofSpaceship,AttackShip,InfiniteCargoShipreturns: 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.
Add an AttackShip class which uses half as much fuel as Spaceship each time the lasers are fired.
AttackShip Specifications
Inherit from the provided
SpaceshipclassAdditional attributes:
roundsthe number of rounds remaining on the ship
__init__method withParameters:
selfnamefuel_capacityrounds
Actions:
Call the
Spaceshipconstructor to set common attributes with the same nameSet the
roundsattribute
fire_roundmethod withParameters:
self
Actions:
Reduces the remaining
roundsby1.
Returns:
None(no return statement required)
fire_lasermethod withParameters:
self
Actions:
Subtract
5units 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