Of course! The term "pattern" in Python can refer to several different things. I'll cover the most common ones, from fundamental programming concepts to advanced design techniques.

Fundamental Programming Patterns (Syntax & Idioms)
These are recurring structures and idioms you'll see constantly in Python code.
a) String Formatting (Pattern for Embedding Variables in Strings)
There are three main ways to do this, with the third being the modern standard.
Old Style Formatting ( operator) The original C-style formatting.
name = "Alice"
age = 30
print("Hello, my name is %s and I am %d years old." % (name, age))
str.format() Method
More flexible and readable than the old style.

name = "Bob"
age = 25
print("Hello, my name is {} and I am {} years old.".format(name, age))
# You can also use positional or keyword arguments
print("Hello, my name is {n} and I am {a} years old.".format(n=name, a=age))
f-Strings (Formatted String Literals) - RECOMMENDED**** Introduced in Python 3.6, f-strings are the most popular, concise, and readable way. They are evaluated at runtime.
name = "Charlie"
age = 35
print(f"Hello, my name is {name} and I am {age} years old.")
# You can even embed expressions!
print(f"Next year, I will be {age + 1} years old.")
b) Looping Patterns (Iterating Over Collections)
Iterating Over a List/Tuple (Standard for loop)
This is the most common pattern.
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit)
Iterating with an Index (enumerate)
When you need both the item and its index.
fruits = ["apple", "banana", "cherry"]
for index, fruit in enumerate(fruits):
print(f"Index {index}: {fruit}")
Iterating Over a Dictionary By default, iterating over a dictionary gives you its keys.

person = {"name": "David", "city": "New York"}
for key in person:
print(key)
# To get key-value pairs:
for key, value in person.items():
print(f"{key}: {value}")
Iterating Over a List with Index and Value (enumerate with start)
A more explicit version of the enumerate pattern.
fruits = ["apple", "banana", "cherry"]
for i in range(len(fruits)):
print(f"Index {i}: {fruits[i]}")
# The enumerate() version is generally preferred for its clarity.
c) Conditional Patterns
if-elif-else Chain
For handling multiple, mutually exclusive conditions.
score = 85
if score >= 90:
grade = 'A'
elif score >= 80:
grade = 'B'
elif score >= 70:
grade = 'C'
else:
grade = 'F'
print(f"Grade: {grade}")
Ternary Operator (Conditional Expression)
A one-line if-else statement for simple assignments.
age = 20 status = "Adult" if age >= 18 else "Minor" print(status)
d) File Handling Pattern (The with statement)
This is the standard, safe way to handle files in Python. The with statement ensures that the file is automatically closed, even if errors occur.
# Recommended pattern
try:
with open('my_file.txt', 'r') as f:
content = f.read()
print(content)
except FileNotFoundError:
print("Error: The file was not found.")
# Old, less safe way (file might not be closed on error)
# f = open('my_file.txt', 'r')
# content = f.read()
# f.close() # Easy to forget this line
Design Patterns (Object-Oriented)
These are well-established, reusable solutions to common software design problems. Python's flexibility allows you to implement them in clean, "Pythonic" ways.
a) Singleton Pattern
Ensures a class has only one instance and provides a global point of access to it.
Pythonic Implementation: Using a module. Since a module is only imported once, its contents act as a natural singleton.
# database.py
class DatabaseConnection:
_instance = None
def __new__(cls):
if cls._instance is None:
print("Creating new database connection...")
cls._instance = super(DatabaseConnection, cls).__new__(cls)
# Initialize the connection here
return cls._instance
def query(self, sql):
print(f"Executing query: {sql}")
# main.py
# from database import DatabaseConnection
# db1 = DatabaseConnection()
# db2 = DatabaseConnection()
# print(db1 is db2) # Output: True
# db1.query("SELECT * FROM users")
b) Factory Pattern
Defines an interface for creating an object, but lets subclasses decide which class to instantiate. It promotes loose coupling.
Pythonic Implementation: Using a simple function that returns an instance of a class.
from enum import Enum
class PizzaType(Enum):
MARGHERITA = "margherita"
PEPPERONI = "pepperoni"
# --- Concrete Products ---
class MargheritaPizza:
def prepare(self):
return "Preparing Margherita Pizza"
class PepperoniPizza:
def prepare(self):
return "Preparing Pepperoni Pizza"
# --- The Factory ---
def pizza_factory(pizza_type: PizzaType):
if pizza_type == PizzaType.MARGHERITA:
return MargheritaPizza()
elif pizza_type == PizzaType.PEPPERONI:
return PepperoniPizza()
else:
raise ValueError("Unknown pizza type")
# --- Client Code ---
order1 = pizza_factory(PizzaType.MARGHERITA)
print(order1.prepare()) # Output: Preparing Margherita Pizza
order2 = pizza_factory(PizzaType.PEPPERONI)
print(order2.prepare()) # Output: Preparing Pepperoni Pizza
c) Observer Pattern
Defines a one-to-many dependency between objects. When one object (the subject) changes its state, all its dependents (observers) are notified.
Pythonic Implementation: Using Python's built-in Observer pattern from the abc module or a simple list of callbacks.
from abc import ABC, abstractmethod
# --- Observer Interface ---
class Observer(ABC):
@abstractmethod
def update(self, message: str):
pass
# --- Concrete Observers ---
class UserEmailNotifier(Observer):
def update(self, message: str):
print(f"Sending email notification: {message}")
class MobileAppNotifier(Observer):
def update(self, message: str):
print(f"Sending push notification to app: {message}")
# --- Subject (the one being observed) ---
class NewsAgency:
def __init__(self, name: str):
self.name = name
self._observers = []
self._latest_news = None
def attach(self, observer: Observer):
if observer not in self._observers:
self._observers.append(observer)
def detach(self, observer: Observer):
try:
self._observers.remove(observer)
except ValueError:
pass
def notify(self):
for observer in self._observers:
observer.update(self._latest_news)
def add_news(self, news: str):
print(f"\n{self.name} has new news!")
self._latest_news = news
self.notify()
# --- Client Code ---
news_agency = NewsAgency("Tech Times")
user1 = UserEmailNotifier()
app1 = MobileAppNotifier()
news_agency.attach(user1)
news_agency.attach(app1)
news_agency.add_news("New Python version released!")
news_agency.detach(user1)
news_agency.add_news("Design Patterns are important.")
Data Analysis Patterns (Pandas)
When working with data in pandas, certain patterns are essential for manipulating and analyzing data.
a) The Chain Pattern (Method Chaining)
Pandas is designed for method chaining, where you perform a series of operations on a DataFrame in a single, readable line.
import pandas as pd
# Sample data
data = {'Name': ['Alice', 'Bob', 'Charlie', 'David'],
'Age': [25, 30, 35, 28],
'City': ['New York', 'Los Angeles', 'Chicago', 'New York']}
df = pd.DataFrame(data)
# Pattern: Filter, then select, then sort
result = (df
.query('Age > 26') # Filter rows
.loc[:, ['Name', 'City']] # Select columns
.sort_values(by='Name') # Sort by a column
)
print(result)
b) The Group-By-Aggregate Pattern
This is the cornerstone of data summarization. You split data into groups, apply a function, and combine the results.
# Sample sales data
sales_data = {'Product': ['A', 'B', 'A', 'C', 'B', 'A'],
'Sales': [100, 150, 200, 50, 120, 180]}
sales_df = pd.DataFrame(sales_data)
# Pattern: Group by 'Product' and calculate total sales
total_sales_per_product = sales_df.groupby('Product')['Sales'].sum()
print(total_sales_per_product)
Summary Table
| Category | Pattern Name | Pythonic Example | Use Case |
|---|---|---|---|
| Fundamental | String Formatting | f"Hello, {name}" |
Embedding variables in strings. |
| Looping with Index | for i, item in enumerate(my_list): |
Needing both index and value in a loop. | |
| Safe File Handling | with open('file.txt') as f: |
Ensuring resources are managed correctly. | |
| Design (OOP) | Singleton | Module-level variables or a class with __new__. |
Ensuring only one instance of a class exists. |
| Factory | def create_animal(animal_type): return Animal(...) |
Creating objects without specifying the exact class. | |
| Observer | A list of update methods that get called on change. |
Decoupling objects so one can notify many others. | |
| Data Analysis | Method Chaining | df.query(...).loc[...].sort_values(...) |
Performing multiple DataFrame operations cleanly. |
| Group-By-Aggregate | df.groupby('col').agg({'col2': 'sum'}) |
Summarizing data based on categories. |
