Sitemap
Tani David

Tani David

I am a passionate Full-Stack Software AI Engineer | UI UX & Graphic Designer with a strong foundation in philosophy

Press enter or click to view image in full size
Image from Pinterest, designed by OOO

Modern Python Approach

5 min readApr 19, 2025

--

Modern Object‑Oriented Programming Made Simple

1. Introduction to OOP & Type Hints

Theory:

  • Classes are templates for creating objects (e.g., UgaliRecipe blueprint).
  • Objects are class instances with unique data (e.g., ugali_for_4 = Ugali(servings=4)).
  • Type Hints explicitly declare data types for:
  • Parameters (def __init__(self, name: str))
  • Return values (-> None)
  • Variables (price: float = 149.99)

2. Classes & Objects with Type Hints

class Chef:
def __init__(self, name: str, specialty: str) -> None:
self.name: str = name
self.specialty: str = specialty

def introduce(self) -> None:
print(f"I'm Chef {self.name}, expert in {self.specialty}!")

# Create instance
jambo: Chef = Chef("Shaba-2", "African Cuisine")
jambo.introduce()

Terminal Output:

I'm Chef Shaba-2 , expert in African Cuisine!

Type Hints Breakdown:

  • name: str → Parameter must be string
  • -> None → Method returns nothing
  • Shaba-2: Chef → Variable holds Chef instance

3. Attributes & Methods

Instance Method:

class UgaliPot:
def __init__(self, material: str) -> None:
self.material: str = material

def describe(self) -> str:
return f"Traditional {self.material} pot for perfect Ugali"

pot = UgaliPot("clay")
print(pot.describe())

Terminal Output:

Traditional clay pot for perfect Ugali

Type Hint: Method returns string (-> str)

Class Method:

class Restaurant:
chain_name: str = "Nyiragongo Flavors"

@classmethod
def change_chain(cls, new_name: str) -> None:
cls.chain_name = new_name

Restaurant.change_chain("Virunga's food")
print(Restaurant.chain_name)

Terminal Output:

Virunga's food

Static Method:

class CookingUtils:
@staticmethod
def is_hot(temp: float) -> bool:
return temp > 60

print(CookingUtils.is_hot(75.5))

Terminal Output:

True

4. Encapsulation & Abstraction

class SecretRecipe:
def __init__(self) -> None:
self.__secret_spice = "Baobab Powder"

@property
def spice(self) -> str:
return "Secret ingredient protected!"

def reveal(self, password: str) -> None:
if password == "JikoniMagic":
print(f"Secret spice: {self.__secret_spice}")

recipe = SecretRecipe()
print(recipe.spice) # Access via property
recipe.reveal("wrongpass") # Won't reveal

Terminal Output:

Secret ingredient protected!

Type Hints:

  • -> str for property getter
  • password: str enforces string input

OOP Concepts:

  • Encapsulation: __secret_spice is hidden
  • Abstraction: Exposes simple spice property

5. Inheritance & Polymorphism

class Dish:
def serve(self) -> str:
raise NotImplementedError

class Ugali(Dish):
def serve(self) -> str:
return "Serving with Nderema!"

class Chapati(Dish):
def serve(self) -> str:
return "Serving rolled with Maharagi!"

menu = [Ugali(), Chapati()]
for item in menu:
print(item.serve())

Terminal Output:

Serving with Nderema!
Serving rolled with Maharagi!

Type Hints:

  • Consistent -> str across hierarchy

OOP Concepts:

  • Inheritance: Child classes extend Dish
  • Polymorphism: Different serve() implementations

6. Modern Python Features

Dataclass:

from dataclasses import dataclass

@dataclass
class Customer:
name: str
loyalty_points: int = 0

mama_ngina = Customer("Nyaba-2", 150)
print(mama_ngina)

Terminal Output:

Customer(name='Nyaba-2', loyalty_points=150)

Type Hints with Collections:

from typing import Dict, Tuple

class MealPrep:
def __init__(self):
self.orders: Dict[int, Tuple[str, str]] = {}

def add_order(self, table: int, main: str, soup: str) -> None:
self.orders[table] = (main, soup)

prep_station = MealPrep()
prep_station.add_order(3, "Ugali", "Supu ya Nyama")
print(prep_station.orders)

Terminal Output:

{3: ('Ugali', 'Supu ya Nyama')}

7. Best Practices

Composition Example:

class SpiceBlender:
def grind(self) -> None:
print("Grinding spices...")

class StewPot:
def __init__(self):
self.blender = SpiceBlender()

def cook_stew(self) -> None:
self.blender.grind()
print("Simmering stew!")

pot = StewPot()
pot.cook_stew()

Terminal Output:

Grinding spices...
Simmering stew!

8. Project: African Restaurant System

@dataclass
class Order:
table: int
main_dish: str
soup: str
urgent: bool = False

class Kitchen:
def __init__(self):
self.orders: list[Order] = []

def prioritize_order(self, order: Order) -> None:
if order.urgent:
self.orders.insert(0, order)
else:
self.orders.append(order)
# Usage
order1 = Order(2, "Chapati", "Lentil Soup")
order2 = Order(5, "Ugali", "Beef Soup", urgent=True)
kitchen = Kitchen()
kitchen.prioritize_order(order1)
kitchen.prioritize_order(order2)
print([(o.table, o.urgent) for o in kitchen.orders])

Terminal Output:

[(5, True), (2, False)]

Type Hints:

  • order: Order ensures only Order objects accepted
  • list[Order] specifies collection type

9. Exercises

Exercise 1 Solution:

Get Tani David’s stories in your inbox

Join Medium for free to get updates from this writer.

Here’s a modern OOP implementation using Python type hints, modeling an African restaurant preparing Ugali and Supu:

from abc import ABC, abstractmethod
from typing import List, Dict, Optional

class Dish(ABC):
"""Abstract base class for all dishes"""
def __init__(self, name: str, ingredients: Dict[str, str], cook_time: int):
self.name: str = name
self.ingredients: Dict[str, str] = ingredients
self.cook_time: int = cook_time # in minutes

@abstractmethod
def prepare(self) -> None:
"""Prepare the dish before cooking"""
pass

@abstractmethod
def cook(self) -> None:
"""Cook the dish"""
pass

def __repr__(self) -> str:
return f"{self.name} ({self.cook_time} mins)"

class Ugali(Dish):
"""Concrete class for Ugali dish"""
def __init__(self):
super().__init__(
name="Ugali",
ingredients={
"maize_flour": "2 cups",
"water": "4 cups"
},
cook_time=20
)

def prepare(self) -> None:
print("Mixing maize flour with cold water to make paste")

def cook(self) -> None:
print(f"Boiling water and stirring vigorously for {self.cook_time} minutes")
print("Forming thick consistency with wooden paddle")

class Supu(Dish):
"""Concrete class for vegetable/meat soup"""
def __init__(self, has_meat: bool = True):
super().__init__(
name="Supu ya Nyama" if has_meat else "Supu ya Mboga",
ingredients=self._get_ingredients(has_meat),
cook_time=45
)
self.has_meat: bool = has_meat

def _get_ingredients(self, has_meat: bool) -> Dict[str, str]:
base = {
"vegetables": "assorted (kale, carrots, onions)",
"spices": "cumin, coriander, pepper",
"water": "2 liters"
}
if has_meat:
base["meat"] = "1 kg beef"
return base

def prepare(self) -> None:
print("Chopping vegetables and meat" if self.has_meat else "Chopping vegetables")
print("Preparing spice mixture")

def cook(self) -> None:
print(f"Simmering {'meat and ' if self.has_meat else ''}vegetables")
print("Adding spices gradually while stirring")

class Meal:
"""Represents a complete meal combination"""
def __init__(self, dishes: List[Dish]):
self.dishes: List[Dish] = dishes

@property
def total_cooking_time(self) -> int:
return sum(dish.cook_time for dish in self.dishes)

def prepare_meal(self) -> None:
print("=== Meal Preparation ===")
for dish in self.dishes:
print(f"\nPreparing {dish.name}:")
dish.prepare()
dish.cook()
print(f"\nMeal ready in {self.total_cooking_time} minutes!")

class AfricanRestaurant:
"""Manages restaurant operations"""
def __init__(self):
self.menu: List[Dish] = [
Ugali(),
Supu(has_meat=True),
Supu(has_meat=False)
]

def prepare_combo_meal(self, dish_names: List[str]) -> Optional[Meal]:
selected_dishes = []
for name in dish_names:
dish = next((d for d in self.menu if d.name == name), None)
if dish:
selected_dishes.append(dish)
else:
print(f"⚠️ {name} not in menu!")
return None
return Meal(selected_dishes)

# Usage Example
if __name__ == "__main__":
# Initialize restaurant
nairobi_kitchen = AfricanRestaurant()

# Customer orders
meal = nairobi_kitchen.prepare_combo_meal(["Ugali", "Supu ya Nyama"])

# Prepare and serve meal
if meal:
print("🍴 Virunga Kitchen Order 🍴")
print(f"Menu: {[dish.name for dish in nairobi_kitchen.menu]}")
print(f"\nPreparing your meal with: {[d.name for d in meal.dishes]}")
meal.prepare_meal()

Key OOP Concepts Demonstrated:

  1. Abstraction (Dish ABC):
  • Defines interface for all dishes
  • Enforces implementation of prepare() and cook()

2. Inheritance (Ugali & Supu):

  • Inherit from Dish base class
  • Implement abstract methods differently

3. Encapsulation:

  • Private method _get_ingredients() in Supu
  • Protected attributes with type hints

4. Polymorphism:

  • Meal class handles different dish types uniformly
  • Restaurant processes different dish implementations the same way

5. Composition:

  • Meal composed of multiple Dish objects
  • Restaurant contains menu items

Modern Python Features:

  • Type hints for all methods and attributes
  • Dictionary literals for ingredients
  • Property decorator for calculated values
  • Generics in type hints (List[Dish])

Sample Output:

🍴 Virunga Kitchen Order 🍴
Menu: ['Ugali', 'Supu ya Nyama', 'Supu ya Mboga']

Preparing your meal with: ['Ugali', 'Supu ya Nyama']

=== Meal Preparation ===

Preparing Ugali:
Mixing maize flour with cold water to make paste
Boiling water and stirring vigorously for 20 minutes
Forming thick consistency with wooden paddle
Preparing Supu ya Nyama:
Chopping vegetables and meat
Preparing spice mixture
Simmering meat and vegetables
Adding spices gradually while stirring
Meal ready in 65 minutes!

Exercises to Try:

  1. Add a new dish Kanga-journé to the menu
  2. Implement a VegetarianMeal subclass with validation
  3. Add portion sizes and calculate nutritional information
  4. Create a OrderSystem class that tracks multiple meals

Type Hints: Field types and method return type

Key Type Hint Takeaways:

  1. Always declare parameter types: (self, name: str)
  2. Specify return types: -> None or -> float
  3. Annotate variables: price: float = 9.99
  4. Use typing module for collections: List[Order]

--

--

Written by Tani David

I am a passionate Full-Stack Software AI Engineer | UI UX & Graphic Designer with a strong foundation in philosophy