In a previous challenge, we explored how to represent financial assets like stocks and bonds using inheritance in Python. This blog post reviews a solution to that challenge, where we created a class structure that leverages inheritance to group common properties while ensuring that subclasses implement specific behaviors. Let’s dive into the solution and see how it addresses the requirements.
Problem Recap
The challenge was to create a class structure with the following requirements:
- Base Class: A common base class (
Asset
) should be created to hold shared attributes such asprice
. - Stock Class: A subclass (
Stock
) should represent a stock, including attributes likecompany_name
andticker_symbol
. - Bond Class: Another subclass (
Bond
) should represent a bond, including attributes likedescription
,duration
, andyield_percentage
. - Abstract Base Class: The base class should be abstract, meaning it cannot be instantiated directly, and it should enforce that subclasses override the
get_description
method. - Specific Implementations: Each subclass should implement the
get_description
method to return a string that provides information specific to that asset type.
Step-by-Step Solution
Let’s break down the solution into key components.
1. Creating the Abstract Base Class
The first step was to define an abstract base class Asset
. This class includes the price
attribute and an abstract method get_description
, which must be implemented by any subclass.
from abc import ABC, abstractmethod
class Asset(ABC):
def __init__(self, price):
self.price = price
@abstractmethod
def get_description(self):
pass
By using the ABC
(Abstract Base Class) module, we ensure that Asset
cannot be instantiated directly. The @abstractmethod
decorator is used to enforce that any subclass of Asset
must implement the get_description
method.
2. Implementing the Stock Class
Next, the Stock
class was created as a subclass of Asset
. This class introduces additional attributes, company_name
and ticker_symbol
, and provides its own implementation of get_description
.
class Stock(Asset):
def __init__(self, price, company_name, ticker_symbol):
super().__init__(price)
self.company_name = company_name
self.ticker_symbol = ticker_symbol
def get_description(self):
return f"Stock: {self.company_name} ({self.ticker_symbol}), Price: ${self.price:.2f}"
The get_description
method in the Stock
class returns a formatted string that includes details about the stock.
3. Implementing the Bond Class
Similarly, the Bond
class was created as a subclass of Asset
. It introduces description
, duration
, and yield_percentage
as additional attributes, and provides its own implementation of get_description
.
class Bond(Asset):
def __init__(self, price, description, duration, yield_percentage):
super().__init__(price)
self.description = description
self.duration = duration
self.yield_percentage = yield_percentage
def get_description(self):
return (f"Bond: {self.description}, Duration: {self.duration} years, "
f"Yield: {self.yield_percentage:.2f}%, Price: ${self.price:.2f}")
The get_description
method in the Bond
class returns a formatted string that includes details about the bond.
Testing the Solution
To test the solution, we can create instances of Stock
and Bond
and then call the get_description
method on each. Attempting to instantiate the Asset
class directly should result in an error.
if __name__ == "__main__":
try:
# This should raise an error because Asset is abstract
asset = Asset(1000)
except TypeError as e:
print(f"Error: {e}")
# Create a Stock instance
apple_stock = Stock(150.00, "Apple Inc.", "AAPL")
print(apple_stock.get_description())
# Create a Bond instance
treasury_bond = Bond(1000.00, "U.S. Treasury Bond", 10, 1.5)
print(treasury_bond.get_description())
Running the code above produces the following output:
Error: Can't instantiate abstract class Asset with abstract method get_description
Stock: Apple Inc. (AAPL), Price: $150.00
Bond: U.S. Treasury Bond, Duration: 10 years, Yield: 1.50%, Price: $1000.00
As expected, attempting to instantiate Asset
directly results in a TypeError
, while the Stock
and Bond
instances provide specific descriptions based on their respective attributes.