🌿 Valleys & Bytes

Navigate
💻Source Code 📖Explanation 🧠Quiz ⚠️Mistakes 📋Cheatsheet 🤖AI Share 💬Comments 📬Subscribe
Quick Actions
Browse by Topic

📦 Part of the Encapsulation series

← Back to Encapsulation Hub
🐍 Python OOP Tutorial

FlowerShop Program
Fully Explained

A complete, line-by-line walkthrough of a Python OOP program — every method, every decision, every concept clarified from the ground up. Built with Philippine Pesos and real-world patterns you'll use in production.

class __init__ while True try / except f-strings dict self PHP ₱ OOP __name__

📄 Full Source Code

Complete, syntax-highlighted Python — copy or download below

flower_shop.py
1 class FlowerShop:
2     def __init__(self):
3         self.flowers = {
4             "Rose":      50.00,
5             "Tulip":     40.00,
6             "Orchid":    60.00,
7             "Sunflower": 30.00,
8             "Lily":      55.00
9         }
10
11     def display_available_flowers(self):
12         """Display the list of available flowers with their prices."""
13         print("Available Flowers in the Shop:")
14         for flower, price in self.flowers.items():
15             print(f"{flower}: PHP {price:.2f} per stem")
16
17     def get_chosen_flower(self):
18         """Prompt the user to choose a flower from the available list."""
19         while True:
20             chosen_flower = input("Enter the flower name: ").strip().title()
21             if chosen_flower in self.flowers:
22                 return chosen_flower
23             else:
24                 print("Invalid flower. Please choose from the list.")
25
26     def get_quantity(self):
27         """Prompt the user to enter the quantity of flowers."""
28         while True:
29             try:
30                 quantity = int(input("Enter the quantity: "))
31                 if quantity > 0:
32                     return quantity
33                 else:
34                     print("Quantity must be a positive integer.")
35             except ValueError:
36                 print("Invalid input. Please enter a positive integer.")
37
38     def calculate_total_cost(self, flower, quantity):
39         """Calculate and return total cost based on flower and quantity."""
40         price_per_stem = self.flowers[flower]
41         total_cost = price_per_stem * quantity
42         return total_cost
43
44     def run(self):
45         """Main method to run the flower shop program."""
46         self.display_available_flowers()
47         chosen_flower = self.get_chosen_flower()
48         quantity = self.get_quantity()
49         total_cost = self.calculate_total_cost(chosen_flower, quantity)
50         print(f"\nChosen Flower: {chosen_flower}")
51         print(f"Quantity: {quantity}")
52         print(f"Total Cost: PHP {total_cost:.2f}")
53
54 if __name__ == "__main__":
55     shop = FlowerShop()
56     shop.run()
💡 About This Program

What You'll Learn

This program is a gateway into real-world Python concepts. Here's why it matters for every beginner learning OOP.

🏗️
Foundation
Object-Oriented Programming
FlowerShop is a textbook OOP example — one class, clean methods, and shared state through self. Master this and every Python project becomes approachable.
🛡️
Defensive Coding
Input Validation Patterns
The while True + try/except pattern used here is the gold standard for CLI input validation — used in production tools, not just beginner code.
💰
Real World
Currency & Formatting
The :.2f f-string format used for Philippine Pesos (PHP) is the exact technique used in e-commerce apps and financial dashboards.
§ 01

Program Overview

The FlowerShop program simulates a real-world flower purchasing system using Object-Oriented Programming — a class that bundles all related data and behaviour into one organized, reusable unit.

The user flow: browse available flowers → choose one → specify a quantity → receive a total cost in Philippine Pesos (PHP ₱).

What makes this program an excellent learning tool is that it is small enough to fully understand in one sitting, yet it demonstrates every major concept you will encounter in real Python applications: classes, constructors, instance variables, input validation, exception handling, dictionary operations, and f-string formatting — all working together in a coherent, purposeful flow.

💡
Why OOP?

Using a class instead of loose functions keeps all shop logic self-contained and easy to extend. Each method has exactly one responsibility — a principle called Single Responsibility. If you ever need to add a discount system, a loyalty program, or a receipt printer, you add a method — you do not rewrite the whole program.

🎯
What to focus on

As you read through the explanations, ask yourself: "Why was this designed this way?" Not just "what does this do?" Understanding intent is the difference between copying code and truly writing it.

§ 02

Class Declaration — class FlowerShop

python
class FlowerShop:
Why This Line Is Used

This declares a blueprint called FlowerShop. Without a class, all variables and functions would be scattered globally — hard to manage, hard to reuse, and impossible to instantiate multiple independent shops.

Think of the class as an architect's drawing. The drawing itself is not a building — it is a precise specification that can be used to construct as many identical buildings as needed. Every time you call FlowerShop(), Python uses this class definition as instructions to build a fresh, independent object in memory.

How It Works

A class is a template. Writing FlowerShop() creates a live instance from it. The instance carries its own data and methods. Note there is no (object) after the class name — in Python 3, all classes implicitly inherit from object, so this is valid, modern Python.

🏗️
Naming Convention

Class names in Python use PascalCase (each word capitalised, no underscores). Functions and variables use snake_case. FlowerShop follows PEP 8 correctly. This consistency is why Python codebases are easy to read across teams and projects.

§ 03

Constructor — __init__

python
def __init__(self):
    self.flowers = {
        "Rose": 50.00, "Tulip": 40.00,
        "Orchid": 60.00, "Sunflower": 30.00, "Lily": 55.00
    }
Why This Is Used

__init__ is the constructor — it fires automatically when a FlowerShop object is created. It builds self.flowers — the shop's inventory, mapping each flower name to its PHP price per stem. The double underscores make this a dunder method (double underscore). Python calls it automatically; you never invoke it directly.

The self Keyword

self refers to the specific instance being created. self.flowers attaches the dictionary to that object so every method in the class can access the same shared data without passing it as an argument each time. self is always the first parameter of every instance method, and Python passes it automatically.

Data TypeWhy It Was Chosen
dict (self.flowers)Maps flower name → price directly; instant O(1) lookup by key
float (50.00)Represents currency with decimal precision for PHP formatting
str keys ("Rose")Human-readable; used for both display and validation simultaneously
📌
Why initialise data inside __init__, not at class level?

Defining self.flowers inside __init__ means every instance gets its own independent copy of the dictionary. If you defined it at class level, all instances would share the same dictionary — a subtle and dangerous bug when you have multiple shop objects modifying inventory.

§ 04

display_available_flowers()

python
def display_available_flowers(self):
    print("Available Flowers in the Shop:")
    for flower, price in self.flowers.items():
        print(f"{flower}: PHP {price:.2f} per stem")
Why This Method Is Used

Before a customer can choose, they must see what is available. This method is the shop's menu board — always the first thing called. Isolating it means display logic can be updated without touching any other method.

#CodeWhat Happens
1.items()Returns each (name, price) pair as a tuple for simultaneous unpacking
2for flower, price in …Unpacks each tuple into two named variables cleanly
3{price:.2f}Format specifier — always shows exactly 2 decimal places (e.g. 50.00)
4print(…)Outputs the formatted string directly to the terminal
🔢
Why :.2f matters for currency

Without this format specifier, Python might print 50.0 instead of 50.00. In a financial context, showing two decimal places is not just stylistic — it communicates precision and professionalism. The same technique is used in every e-commerce checkout, banking app, and invoice system you have ever used.

§ 05

get_chosen_flower()

python
def get_chosen_flower(self):
    while True:
        chosen_flower = input("Enter the flower name: ").strip().title()
        if chosen_flower in self.flowers:
            return chosen_flower
        else:
            print("Invalid flower. Please choose from the list.")
Three Defensive Techniques
TechniqueWhat It Does & Why
.strip()Removes accidental whitespace — " Rose " becomes "Rose"
.title()Normalises case — "rOSE", "rose", "ROSE" all become "Rose"
while True:Infinite loop — only exits via return when valid input is received
in self.flowersDictionary membership check — O(1) speed, clean and readable
Loop Flow Diagram
1
Enter loop
while True begins — runs until explicitly returned from
2
Prompt user
input() waits for the user to type and press Enter
3
Clean input
.strip().title() normalises the text in one chain
4
Validate
in self.flowers checks dictionary membership
Valid → return
Name is sent back to run() and the loop exits immediately
Invalid → repeat
Error printed; loop restarts from Step 2 — no crash possible
§ 06

get_quantity()

python
def get_quantity(self):
    while True:
        try:
            quantity = int(input("Enter the quantity: "))
            if quantity > 0:
                return quantity
            else:
                print("Quantity must be a positive integer.")
        except ValueError:
            print("Invalid input. Please enter a positive integer.")
Two Layers of Protection
Layer 1
try / except ValueError

If the user types "abc" or "3.5", int() raises a ValueError. The except block catches it and prints a helpful message instead of crashing the program entirely.

Layer 2
if quantity > 0

Even if conversion succeeds, 0 or -5 must be rejected. These are logically invalid for a purchase even though they are mathematically valid integers.

⚠️
Why while True again?

The only escape is the return inside if quantity > 0. The loop keeps prompting until both layers of validation pass simultaneously. This guarantees the caller always receives a valid, usable value — never garbage.

§ 07

calculate_total_cost()

python
def calculate_total_cost(self, flower, quantity):
    price_per_stem = self.flowers[flower]
    total_cost = price_per_stem * quantity
    return total_cost
Why Separate This Into Its Own Method?

Single Responsibility Principle — each method does exactly one thing. This makes the calculation easy to test, read, and modify later (for example, adding discounts or taxes) without touching display or input methods.

Imagine a future requirement: "Apply a 10% senior citizen discount." With this design, you only change one method. If the calculation lived inside run(), you would be mixing concerns — hunting through display code to find math code.

#ActionWhat Happens
1Receive argsGets flower (string) and quantity (integer) from the caller
2Dict lookupself.flowers[flower] retrieves the price in O(1) time
3Multiplyprice_per_stem × quantity — e.g. 50.00 × 3 = 150.00
4ReturnSends the number back to run() for display — caller decides what to do
💡
Why return, not print?

Returning gives the caller control. Printing inside the calculator mixes display with logic — a design flaw that reduces flexibility. With return, the same method can be used in a terminal app, a web API, a receipt generator, or a unit test — with zero changes. The caller decides how to use the result.

§ 08

run() — The Orchestrator

python
def run(self):
    self.display_available_flowers()
    chosen_flower = self.get_chosen_flower()
    quantity = self.get_quantity()
    total_cost = self.calculate_total_cost(chosen_flower, quantity)
    print(f"\nChosen Flower: {chosen_flower}")
    print(f"Quantity: {quantity}")
    print(f"Total Cost: PHP {total_cost:.2f}")

run() is the conductor. It calls every other method in the correct sequence. Without it, all individual methods would exist but never be coordinated into a complete user experience. Notice that run() does almost no work itself — it delegates everything.

#CallWhat Happens
1display_available_flowers()Shows the menu so the user knows their options
2get_chosen_flower()Prompts and validates. Returns valid flower name
3get_quantity()Prompts and validates. Returns positive integer
4calculate_total_cost(…)Computes and returns the total cost as a float
5print() × 3Displays the full order summary to the user
🎼
The Orchestrator Pattern

run() reads like plain English: display, choose, get quantity, calculate, print. Each line maps directly to a specific, testable unit of behaviour. If anything breaks, you know exactly which method to investigate — the structure enforces clarity.

§ 09

Entry Point — if __name__ == '__main__'

python
if __name__ == "__main__":
    shop = FlowerShop()
    shop.run()

The entry point — the starting gun. Python sets __name__ to "__main__" only when the file is executed directly, not when imported by another script. This guard prevents accidental execution.

Without this guard, importing flower_shop into a test suite would immediately launch the program and start prompting for input — a catastrophic side effect. The guard is a professional safety mechanism that every production Python script should include.

LineWhat It Does
if __name__ == "__main__":Guard clause — only runs on direct execution, not on import
shop = FlowerShop()Instantiates the object, triggering __init__ and building the dictionary
shop.run()Launches the full user interaction sequence from start to finish
🔍
How does Python know?

When you run python flower_shop.py, Python sets __name__ = "__main__" for that file. When another script does import flower_shop, Python sets __name__ = "flower_shop" instead — and the guard block is skipped entirely. Two characters of context tell Python exactly what role the file is playing.

§ 10

Full Program Execution Flow

From the moment you run python flower_shop.py to the moment Python exits:

1
Python reads the file
class FlowerShop is registered as a blueprint — no code runs yet
2
__name__ check passes
Python confirms direct execution and enters the if block
3
FlowerShop() is called
__init__ fires — self.flowers dictionary is built with 5 entries
4
shop.run() begins
Orchestrator starts by calling display_available_flowers()
5
Menu is printed
All 5 flowers with PHP prices are shown to the user
6
get_chosen_flower() loops
User prompted until a valid, recognised flower name is entered
7
get_quantity() loops
User prompted until a valid positive integer is entered
8
calculate_total_cost() runs
Price looked up; multiplication performed; result returned
Summary printed → program ends cleanly
Flower, quantity, and PHP total displayed; run() finishes; Python exits
§ 11

Key Concepts Summary

ConceptWhere & Why Used
OOP / ClassEntire program — bundles data and methods into one reusable unit
Encapsulationself.flowers is private to the instance, shared across all methods
Constructor (__init__)Guarantees inventory is set up before any method is called
Infinite Loopget_chosen_flower & get_quantity — re-prompts until valid input received
Exception Handlingget_quantity — catches non-integer input, prevents crash
Dictionary Lookupcalculate_total_cost — O(1) price retrieval by key
f-Strings :.2fdisplay & run — currency always shows exactly 2 decimal places
.strip().title()get_chosen_flower — case-insensitive, whitespace-tolerant input
Single ResponsibilityEach method does exactly one thing — no mixed concerns anywhere
__name__ guardEntry point — prevents accidental execution on import
§ 12

Key Takeaways for Beginners

If you take only five things away from this tutorial, make them these:

🏛️

OOP bundles data + behaviour together

FlowerShop holds its own flowers and all related methods. That is encapsulation — the foundation of every Python class you will ever write going forward.

🔄

while True + return = bulletproof input

Never write input() without a validation loop. The pattern in get_chosen_flower() is copy-paste ready for any project at any skill level.

🛡️

try/except isolates expected errors

ValueError from int() is caught, not crashed. Defensive programming means planning for what users will inevitably do wrong.

🧩

Single Responsibility Principle

Each method does one thing. run() orchestrates, calculate_total_cost() calculates, display_…() displays. Change one without breaking the others.

🚪

Always use if __name__ == '__main__'

This guard lets you import your class without accidentally running the whole program. It is the mark of a professional, production-ready Python file.

🧠 Test Yourself

Quick Knowledge Check

Click each question to reveal the answer. Use these to check your understanding before moving on to the next tutorial.

What would happen if you removed the while True loop from get_chosen_flower()?
The function would only ask the user once. If they typed an invalid flower name, the else branch would print an error — but then the function would end and return None. The program would crash when run() tried to use None as a dictionary key. The while True loop is essential for keeping the prompt alive until valid input is received.
Why does get_quantity() need both a try/except and an if quantity > 0 check?
try/except handles the type error: if the user types "abc", int("abc") raises a ValueError before any comparison can happen. But even after a successful int() conversion, the user could type 0 or -3 — valid integers but logically impossible quantities. The if quantity > 0 check catches this second category. You need both layers because they defend against completely different classes of invalid input.
If you created two FlowerShop instances and added a flower to one, would the other be affected?
No. Because self.flowers is defined inside __init__, each instance gets its own independent copy of the dictionary. Modifying shop1.flowers["Carnation"] = 35.00 has zero effect on shop2.flowers. This is why instance variables (defined with self inside __init__) are safer than class-level variables — each object owns its own data.
What does .strip().title() do, and why is the order important?
.strip() removes leading and trailing whitespace first — turning " rose " into "rose". Then .title() capitalises the first letter of each word — turning "rose" into "Rose". The order matters: if you called .title() first on " rose", the leading space could interfere with capitalisation behaviour depending on the Python version. Strip always comes first — clean before you transform.
How would you add a 10% discount for senior citizens to this program?
Add a new method: def apply_discount(self, total, rate=0.10): return total * (1 - rate). Then in run(), ask is_senior = input("Senior citizen? y/n: ").lower() == 'y', and apply: if is_senior: total_cost = self.apply_discount(total_cost). Because calculate_total_cost() returns a value instead of printing directly, you can manipulate the result freely before displaying it. This is exactly why separation of concerns matters.
What does :.2f mean inside an f-string, and when should you use it?
: introduces the format specification. .2 means "2 digits after the decimal point". f means "format as a fixed-point float." Together, :.2f guarantees the number always displays with exactly two decimal places — 50.0 becomes 50.00, and 150.555 becomes 150.56 (rounded). Use it any time you display currency, measurements, or percentages where precision matters to the user.
⚠️ Watch Out

Common Beginner Mistakes

These are the most frequent errors students make when first writing programs like FlowerShop — and how to fix them correctly.

❌ Wrong Printing instead of returning from calculator

Printing inside calculate_total_cost() mixes display logic with business logic. The method becomes impossible to reuse silently in tests, APIs, or receipt systems. Always return computed values.

❌ Bad practice
def calculate_total_cost(self, flower, qty): total = self.flowers[flower] * qty print(f"Total: {total:.2f}") # 🚫
✅ Correct
def calculate_total_cost(self, flower, qty): total = self.flowers[flower] * qty return total # ✓ caller decides
❌ Wrong Not handling invalid input — no try/except

Without a try/except block, typing "abc" when a number is expected will crash the program with an unhandled ValueError. Always wrap int(input()) in a try block.

❌ Crashes on "abc"
quantity = int(input("Quantity: ")) # Unhandled ValueError if not a number
✅ Handles gracefully
try: quantity = int(input("Quantity: ")) except ValueError: print("Numbers only!")
❌ Wrong Defining mutable data at class level

Placing flowers = {} at class level means all instances share the same dictionary. Changing it in one object changes it everywhere — a silent, dangerous bug.

❌ Shared (dangerous)
class FlowerShop: flowers = {"Rose": 50} # 🚫 class-level
✅ Per-instance (safe)
class FlowerShop: def __init__(self): self.flowers = {"Rose": 50} # ✓
❌ Wrong Missing the __name__ guard

Without if __name__ == "__main__":, importing your file in a test or another script immediately executes the program and prompts for input — completely breaking your test suite.

❌ Runs on import
shop = FlowerShop() shop.run() # 🚫 runs immediately always
✅ Safe guard
if __name__ == "__main__": shop = FlowerShop() shop.run() # ✓ direct run only
📋 Quick Reference

Python OOP Cheatsheet

The essential patterns used in FlowerShop — ready to copy into your own projects.

Class + Constructor
class MyClass: def __init__(self): self.data = {}
Always define instance variables in __init__ for safety and clarity.
String Input Validation
while True: val = input("Enter: ").strip().title() if val in valid_set: return val
strip() then title() — always clean before you transform.
Integer Input Validation
while True: try: n = int(input("Qty: ")) if n > 0: return n except ValueError: print("Numbers only")
Two layers: type error first, logic error second.
Currency Formatting
total = 150.5 print(f"PHP {total:.2f}") # → PHP 150.50
:.2f always shows exactly 2 decimal places — essential for money.
Dict Iteration
for key, val in d.items(): print(f"{key}: {val:.2f}")
.items() gives (key, value) tuples for simultaneous unpacking.
Entry Point Guard
if __name__ == "__main__": obj = MyClass() obj.run()
Every Python script file should end with this guard. No exceptions.
Docstrings
def my_method(self): """One-line description here. Longer explanation if needed. """
Triple-quoted strings immediately after def — used by help() and IDEs.
Single Responsibility
# ✓ Each method → one job def display(self): ... def get_input(self): ... def calculate(self): ...
If a method name uses "and", it probably does too much. Split it.
Return vs Print
# ✓ Return — caller decides def calculate(self, qty): return self.price * qty # 🚫 Don't print in calculators
Functions that compute should return. Only UI functions should print.
📚 Deep Dive

Concepts Behind the Code

Understanding why the code is designed this way unlocks every Python project you will ever build.

🏗️
Why OOP for This Program?
A procedural version with loose functions would work, but OOP offers real advantages here that grow more valuable as programs scale.
  • All data (flowers, prices) lives with its methods — nothing is lost or mismatched
  • You can create multiple instances: shop_manila = FlowerShop()
  • Adding features means adding methods, not rewriting existing logic
  • Unit testing is trivial — just instantiate and call a single method
🔒
The Validation Philosophy
This program never crashes from user input. Here is the defensive strategy applied at every input boundary.
  • Sanitise before validate — .strip().title() runs before the if-check
  • Catch type errors first — try/except handles ValueError before logic checks
  • Never trust user input — assume it will be wrong and design for that reality
  • Keep prompting — while True ensures the program never silently gives up
📐
Design Patterns at Work
Professional design patterns visible in this beginner program — the same ones used in enterprise Python codebases.
  • Template Method — run() orchestrates a fixed sequence of abstract steps
  • Single Responsibility — each method does exactly one thing cleanly
  • Guard Clause — __name__ == "__main__" prevents side effects on import
  • Fail Fast — validation happens at the boundary, not deep inside logic
🇵🇭
Philippine Context
This program uses real Philippine Pesos (₱ PHP) and local flower culture — grounding OOP concepts in a familiar, relatable setting.
  • Prices reflect typical Philippine market rates per stem in 2024
  • Orchids (PHP 60) are popular for Filipino celebrations and pasalubong
  • Sunflowers (PHP 30) are the most affordable — accessible for everyday gifting
  • Strong base for a tindahan (local shop) management app with SQLite
🚀 Go Further

Extend This Program

Once you understand FlowerShop, here are natural next steps to level up your Python skills progressively.

🗃️
Level Up
Add a SQLite Database
Replace the hardcoded dictionary with SQLite. Store flowers, prices, and order history — your first real persistence layer. Learn sqlite3 module.
🌐
Web Dev
Build a Flask API
Wrap the FlowerShop logic in a Flask REST API. Add endpoints for listing flowers, placing orders, and getting receipts as JSON — your first web service.
🧪
Best Practices
Write Unit Tests
Use unittest to test calculate_total_cost() and input validation logic. Testing OOP code is a critical skill for any professional Python developer.
🤖 AI-Powered

Share This Article

Generate smart, AI-written captions tailored to each platform — then share with one click to your network.

💬 Community

Discussion

Share your questions, insights, or code experiments. All skill levels are welcome here.

✍️ Leave a Comment

3 Comments

📬 Newsletter

Learn Python,
One Tutorial At a Time

Join 2,400+ developers in the Philippines and beyond. New tutorials, code breakdowns, and project ideas — every week, free.

✓ No spam, ever ✓ Unsubscribe anytime ✓ Free Python resources ✓ Weekly tutorials