class Food:
def __init__(self, name, calories):
self.name = name
self.calories = calories
🧑💻 2.5 Object-Oriented Programming (OOP) Basics
Welcome to the final notebook in the Programming Basics module! In this session, we’ll explore the fundamentals of Object-Oriented Programming (OOP), a powerful approach to writing code that is organised, modular, and reusable.
🎯 Objectives
By the end of this notebook, you should be able to:
- Understand the core concepts of OOP: classes, objects, attributes, methods, and inheritance.
- Write your own classes to model nutrition-related entities like foods, nutrients, and diets.
- Apply OOP principles to create clean, readable, and maintainable code.
🧠 Why Object-Oriented Programming?
In the real world, data isn’t flat—it’s structured and interconnected.
In nutrition science, for example, you might deal with:
- Foods that have a name, nutrient profile, and cost.
- Nutrients that have a name, units, and dietary recommendations.
- Diets that contain multiple foods and require specific calculations.
These aren’t just isolated data points—they are related entities. OOP helps you mirror these real-world relationships in your code.
Instead of managing a mess of separate variables and functions, you can bundle everything into a single unit (a class) and then create instances of that unit (objects) with unique data.
🧰 What Is OOP?
Object-Oriented Programming is based on the idea of “objects”—which combine data (called attributes) and behaviours (called methods). You define a class as a blueprint, then create multiple objects (instances) from it.
This makes it easy to:
- Keep related information together
- Reuse code through inheritance
- Extend or update functionality without rewriting everything
🧪 Example from Nutrition
Imagine a Food
class:
You can now create specific food items:
= Food("Apple", 95)
apple = Food("Banana", 120) banana
Each Food
object carries its own name
and calories
, and you can easily create new ones without writing new functions or variables each time.
🐘 Fun Fact
Think of OOP like a hippo’s diet log—each food entry is an object, and the diet is a class that keeps it all neat and trackable!
Just like real-life items (e.g. hippos, food, nutrients) have attributes (like colour, energy, or weight) and actions (like eat, swim, or digest), objects in Python do too!
🛠 What You’ll Learn in This Notebook
We’ll cover the core building blocks of OOP in Python:
- Classes and Objects: How to define and use them
- Attributes and Methods: How to store and interact with data
- Inheritance: How to reuse code by creating child classes
- Best Practices: Naming, structure, and readability
You’ll also practise applying OOP principles to realistic nutrition problems—think Nutrient
, Meal
, or DietPlan
classes.
# Setup for Google Colab: Ensure environment is ready
# Note: This module (Programming Basics) does not require datasets
print('No dataset required for this notebook 🦛')
# Install required packages for this notebook
%pip install pandas
print('Python environment ready.')
🧱 Python Classes: A Practical Introduction
This section introduces the idea of classes in Python — the foundation of Object-Oriented Programming (OOP). In the context of food and nutrition science, classes allow us to model foods, nutrients, meals, and other complex entities in a structured and reusable way.
🧠 What Is a Class?
A class is a blueprint for creating objects. Think of it as a cookie cutter — it defines the shape, and you can make as many cookies (objects) as you want from it.
Each object created from a class is called an instance. The instance has attributes (data) and methods (functions it can perform).
📦 Defining a Basic Class
Here’s a simple example of a class that represents a FoodItem
.
class FoodItem:
def __init__(self, name, calories):
self.name = name
self.calories = calories
def describe(self):
return f'{self.name} contains {self.calories} kcal per serving.'
__init__
is the constructor, a special method used to initialise the object.self
refers to the current instance being created..describe()
is a method that returns a string description of the food item.
🍏 Creating and Using an Object
Let’s create an instance of FoodItem
.
= FoodItem("Apple", 95)
apple print(apple.describe())
Output:
Apple contains 95 kcal per serving.
🧪 Exercise: Define Your Own Class
Create a class called Drink
with attributes: - name
(e.g., “Orange Juice”) - volume_ml
(e.g., 250) - sugar_g
(e.g., 22)
Add a method sugar_concentration()
that returns grams of sugar per 100 mL.
# Your code here
💡 Show Answer
Solution code below:
class Drink:
def __init__(self, name, volume_ml, sugar_g):
self.name = name
self.volume_ml = volume_ml
self.sugar_g = sugar_g
def sugar_concentration(self):
return (self.sugar_g / self.volume_ml) * 100
= Drink("Orange Juice", 250, 22)
juice print(f'{juice.name}: {juice.sugar_concentration():.1f} g sugar per 100 mL')
🧭 Summary
- A class is a way to bundle data and functionality together.
- Each object is an instance of a class.
- Use
__init__
to define how each object is constructed. - Add methods to define behaviours.
- OOP helps you organise code better, especially in complex projects.
In the next part, we’ll explore methods, attributes, and inheritance in more detail.
🛠️ Adding Methods and Attributes in Python Classes
Once you’ve defined a basic class, you can add more functionality by defining methods (functions inside classes) and attributes (data stored in each object). This makes your classes more useful and more descriptive.
🧃 Example: Extending the Food
Class
Let’s take our basic Food
class and add:
- A new attribute for category (e.g., ‘Fruit’, ‘Vegetable’)
- A method to update the calorie value based on serving size
class Food:
def __init__(self, name, calories, category):
self.name = name
self.calories = calories
self.category = category # new attribute
def describe(self):
return f"{self.name} ({self.category}) has {self.calories} calories per serving."
def scale_calories(self, factor):
self.calories = int(self.calories * factor)
return f"Updated {self.name} to {self.calories} calories."
Now let’s create and use some objects:
= Food("Carrot", 41, "Vegetable")
carrot = Food("Mango", 60, "Fruit")
mango
print(carrot.describe()) # Carrot (Vegetable) has 41 calories per serving.
print(mango.describe()) # Mango (Fruit) has 60 calories per serving.
# Scale the calorie value
print(carrot.scale_calories(2)) # Updated Carrot to 82 calories.
print(carrot.describe()) # Carrot (Vegetable) has 82 calories per serving.
🧠 Best Practices
- Add only the attributes and methods you need—not everything!
- Try to keep your class focused on one concept.
- Use meaningful method names like
describe
,scale_calories
,increase_amount
, etc. - Add a docstring to methods if they are not self-explanatory.
🔍 Hidden Tip: Why use methods instead of changing attributes directly?
Using a method like scale_calories()
lets you control and validate how attributes are changed, and makes the code more readable. It also makes it easier to maintain or adjust later.
🥗 Inheritance in Python: Food and Diet Classes
In Object-Oriented Programming (OOP), inheritance is a powerful concept where one class (called the child or subclass) derives properties and behaviour (methods and attributes) from another class (called the parent or base class).
This helps you avoid writing the same code again, making your code more organised and modular.
🍎 Step-by-Step: Building the Food
Class
First, we define a general class that represents any food item:
# Parent class
class Food:
def __init__(self, name, calories, category):
self.name = name
self.calories = calories
self.category = category
def describe(self):
return f'{self.name} ({self.category}) has {self.calories} calories per serving.'
__init__
is the constructor. It runs when an object is created.self.name
,self.calories
, andself.category
are attributes that belong to a specific object.- The method
describe
returns a string that summarises the object.
🌿 Creating a Subclass: Diet
Now let’s say we want to add an extra piece of information for some foods that are part of a diet plan, such as "Low-Carb"
or "Keto"
. Instead of rewriting the whole Food
class, we create a child class Diet
that inherits from it.
# Child class that inherits from Food
class Diet(Food):
def __init__(self, name, calories, category, diet_type):
# super() refers to the parent class (Food) and calls its constructor
super().__init__(name, calories, category)
self.diet_type = diet_type
# Overriding the describe method to include diet_type
def describe(self):
return f'{self.name} ({self.category}, {self.diet_type}) has {self.calories} calories per serving.'
🧠 What’s Going On Here?
Diet(Food)
means: Diet is a subclass of Food.super().__init__(...)
calls the parent class’s__init__
method so we don’t have to retype thename
,calories
, andcategory
assignments. This is good practice and prevents duplication.- We then add a new attribute
diet_type
specific to theDiet
class. - We also override the
describe
method to include the diet type.
🧪 Testing the Subclass
= Diet("Avocado", 160, "Fruit", "Keto")
keto_avocado print(keto_avocado.describe())
Output:
Avocado (Fruit, Keto) has 160 calories per serving.
This shows that the new class works just like Food
, but with extra features.
✅ Summary
Term | Meaning |
---|---|
class |
A blueprint for creating objects |
__init__ |
A constructor that runs when the object is created |
super() |
A function that gives access to the parent class’s methods |
Inheritance | A way for one class to reuse or extend another class’s code |
Override | Redefining a method in the child class to change its behaviour |
🧾 Summary: Object-Oriented Programming in Python
By now, you’ve learned the foundational concepts of Object-Oriented Programming (OOP), including:
- Classes: Blueprints for creating objects (like templates for food items).
- Objects: Instances of classes with their own data (attributes) and actions (methods).
- Attributes: Variables that belong to a class/object.
- Methods: Functions that belong to a class/object and operate on its data.
- Inheritance: Allows a new class to inherit methods and attributes from an existing class, promoting code reuse.
🧪 Exercises
1. Custom Food Class
Create a Snack
class that inherits from Food
and includes: - A new attribute snack_type
(e.g., ‘sweet’, ‘salty’) - A method is_healthy()
that returns True
if calories are below 200.
Test it with examples like:
= Snack('Popcorn', 150, 'Grain', 'salty')
popcorn print(popcorn.is_healthy()) # Should return True
2. Composite Object
Create a Meal
class that: - Takes a list of Food
or Diet
objects - Has a method total_calories()
that sums their calories
3. Describe All
Create a function describe_meal(meal)
that loops through the foods in a Meal
object and prints their descriptions.
✅ Conclusion
Object-Oriented Programming makes your Python code: - Easier to organise - More reusable - Cleaner and more intuitive
You can now model real-world nutrition problems in a structured and scalable way.
🔍 Advanced: Magic Methods and Encapsulation
- Magic methods like
__str__
or__repr__
let you control how objects are printed. - Encapsulation helps you protect internal states with naming conventions (e.g.,
_private
,__very_private
). - You can also use
@property
decorators to create computed attributes.
🎉 Well done! You’re ready to tackle real-world nutrition modelling using OOP!