- 🧣 Wrap an object to add behavior — without changing it
- 🔁 Stack multiple decorators — like layers of clothing
- 🎯 Python sugar:
@decoratorabove a function - 📑 Use it for logging, auth, caching, retries, timing…
This presentation is designed for desktop or projector displays.
← Back to curriculumPython Object Oriented Programming
Course 11
← → to navigate
Where we are in the journey
Which design pattern does this code demonstrate?
def log_calls(func): def wrapper(*args, **kwargs): print(f"calling {func.__name__}") return func(*args, **kwargs) return wrapper @log_calls def save(data): ...
A) Observer
B) Strategy
C) Decorator
D) Iterator
Decorator wraps a function to add behavior without modifying it. Python's @ syntax is sugar over save = log_calls(save).
Recap: Lecture 10
@decorator above a function
What is the purpose of Mock() in this test?
from unittest.mock import Mock db = Mock() db.fetch.return_value = {"id": 1, "name": "Alice"} service = UserService(db) user = service.get_user(1) db.fetch.assert_called_once_with(1)
A) To speed up tests by parallelization
B) Replace a real dependency with a controllable fake
C) Generate random test data automatically
D) Save real database calls to disk
Mocks isolate the unit under test: no real DB, no real network, no real file. You can program return values and verify how the dependency was called.
Recap: Lecture 09
@pytest.fixture — reusable setup, runs per test@pytest.fixture def empty_account(): return Account() def test_deposit(empty_account): empty_account.deposit(100) assert empty_account.balance == 100
In a UML diagram, what does the multiplicity 1..* mean?
A) Exactly one
B) Zero or one (optional)
C) One or more (at least one)
D) Any number, including zero
Cheat sheet: 1 = exactly one · 0..1 = optional · 1..* = one or more · * = any number, zero allowed.
Recap: Lecture 08
How many instances of one class relate to another
Common notations:
1 — exactly one0..1 — zero or one1..* — one or more* — zero or moren..m — specific range🔥 numpy · pandas · matplotlib · requests
⏱️ datetime · unittest / pytest
Section 1
"A bag of useful tools you reach into when you need them."
A library is a set of related, low-level components that developers invoke to achieve a specific outcome.
Common library tasks:
1. 📦 Install — download it once
pip install pandas — that's all you need to get started.
2. 📥 Import — bring it into your namespace
Whole module, single function, or with an alias: import pandas as pd
3. 📖 Read the docs & use it
Every library has docs with parameters, return values, examples. Bookmark them — then call its functions and classes like your own code.
numpy · pandas · scipy
matplotlib · seaborn
scikit-learn · pytorch
requests · httpx
beautifulsoup · lxml
websockets · aiohttp
pytest · unittest
black · ruff · mypy
logging · datetime
🔥 500,000+ packages on PyPI — the Python Package Index
A plotting library for static, animated, and interactive visualizations in Python.
numpy is the fundamental package for scientific computing in Python.
import numpy as np x = np.arange(15).reshape(3, 5) x[1:, ::2] = -99 # [[ 0 1 2 3 4] # [-99 6 -99 8 -99] # [-99 11 -99 13 -99]] x.max(axis=1) # array([ 4, 8, 13]) # vectorized math — one operation, every element prices = np.array([10, 20, 30, 40]) prices * 1.21 # add 21% VAT to every price # array([12.1, 24.2, 36.3, 48.4]) # stats over a whole array, no for-loop grades = np.array([72, 85, 49, 91, 60]) grades.mean() # 71.4 (grades >= 50).sum() # 4 students passed
Makes HTTP & HTTPS requests a breeze. Functions for interacting with web APIs and managing responses.
GET, POST, PUT, DELETE — one-linersimport requests url = "https://api.github.com/users/torvalds" response = requests.get(url) if response.status_code == 200: data = response.json() print(f"Name: {data['name']}") print(f"Followers: {data['followers']}") else: print(f"Failed: {response.status_code}")
A standard library module — no install needed. Handles dates, times, durations, time zones.
date, time, datetimetimedelta for arithmetic on timestimezone — never store naive UTC times!datetime + UTC storage saves you from real bugs at 03:00 on DST night.from datetime import datetime, date, time, timezone date1 = date(2026, 5, 7) time1 = time(14, 30) # Always use timezone-aware datetimes! dt = datetime(2026, 5, 7, 14, 30, tzinfo=timezone.utc) now = datetime.now(timezone.utc) delta = now - dt print(f"Date: {date1}") print(f"Now (UTC): {now}") print(f"Delta: {delta}")
stdlib
class + setUpclass TestMath(unittest.TestCase): def test_add(self): self.assertEqual(1+1, 2)
pip install
pytest-cov, pytest-mock)def test_add(): assert 1 + 1 == 2
✅ Most modern Python projects use pytest — less ceremony, better output, huge ecosystem.
Spotlight
A superpower for working with tables of data.
Excel, supercharged. With code.
🔗 pandas.pydata.orgA CSV is just plain text — values separated by commas. Here's cars.csv:
Make,Model,Year,Color,Mileage,Price Toyota,Corolla,2020,Black,45000,15500 Honda,Civic,2018,Red,62000,13200 Toyota,RAV4,2022,White,21000,26800 Ford,Focus,2019,Black,78000,9900 Honda,Accord,2021,Black,32000,22100 Ford,Mustang,2023,Red,8500,41700
📝 First line = column names. Each row below = one record. pd.read_csv() turns this into a DataFrame — a table with named columns and types.
import pandas as pd df_cars = pd.read_csv("cars.csv") print(df_cars)
Make Model Year Color Mileage Price 0 Toyota Corolla 2020 Black 45000 15500 1 Honda Civic 2018 Red 62000 13200 2 Toyota RAV4 2022 White 21000 26800 3 Ford Focus 2019 Black 78000 9900 4 Honda Accord 2021 Black 32000 22100 5 Ford Mustang 2023 Red 8500 41700
🔎 Useful next steps: df.head(), df.info(), df.describe().
Find black cars from 2020 onwards:
import pandas as pd df_cars = pd.read_csv("cars.csv") mask = (df_cars["Color"] == "Black") & (df_cars["Year"] >= 2020) black_2020 = df_cars[mask] print(black_2020)
Make Model Year Color Mileage Price 0 Toyota Corolla 2020 Black 45000 15500 4 Honda Accord 2021 Black 32000 22100
💡 Boolean indexing is vectorized — no Python loops, fast on millions of rows.
Average mileage and price per make:
avg = (df_cars
.groupby("Make")
.agg({"Mileage": "mean", "Price": "mean"})
.rename(columns={
"Mileage": "Avg Mileage",
"Price": "Avg Price",
}))
print(avg)
Avg Mileage Avg Price Make Ford 43250.0 25800.0 Honda 47000.0 17650.0 Toyota 33000.0 21150.0
📊 groupby().agg() is the SQL-ish workhorse of pandas. Built-in aggregations: mean, sum, min, max, count, std…
How many cars per color?
color_counts = df_cars["Color"].value_counts() print(color_counts) # Black 3 # Red 2 # White 1
Newest car per make:
newest = (df_cars
.sort_values("Year", ascending=False)
.groupby("Make", as_index=False)
.first())
print(newest[["Make", "Model", "Year"]])
# Ford Mustang 2023
# Honda Accord 2021
# Toyota RAV4 2022
Calculate depreciation (10% per year) — add a new column:
YEAR_NOW = 2026 df_cars["Depreciation"] = ( df_cars["Price"] * 0.1 * (YEAR_NOW - df_cars["Year"]) ) print(df_cars[["Make", "Model", "Year", "Price", "Depreciation"]])
Write the updated table to a new CSV:
df_cars.to_csv("updated_cars.csv", index=False)
for loop. That's the pandas way.⚡
Complex transformations in a few lines — saves hours.
🧩
Data scientist, analyst, ETL engineer — one tool for all.
🔌
numpy · matplotlib · scikit-learn · sqlalchemy.
Section 2
"A skeleton you fill in — the recipe you follow."
A framework provides the foundational structure for developing software.
🍳
Library = Kitchen tools
📖
Framework = Recipe book
Same kitchen, different kind of help.
The Hollywood Principle
a.k.a. Inversion of Control (IoC)
Library
# your_app.py import requests def main(): r = requests.get(URL) Database.save(r.json()) main() # YOU run the show
👉 You decide when and which functions to call.
Framework
# views.py (Django) def book_list(request): return render(...) # Django runs the loop — # calls book_list when needed
👉 You write the parts, the framework runs them.
| Library | Framework | |
|---|---|---|
| Control flow | You call it | It calls you |
| Scope | Specific operations | Full app architecture |
| Complexity | Smaller, focused | Larger, opinionated |
| Replaceability | Swap easily | Hard to migrate away |
| Examples | numpy, requests, pandas | Django, Flask, FastAPI |
📊
Map Python objects to database tables — SQL without writing SQL.
User.objects.filter(...)
🔗
URL patterns → functions. Defines what runs for each request.
/books/<id> → book_view
🎨
Render data into HTML, JSON, emails — with placeholders & loops.
{{ user.name }}
✅ Plus: auth, sessions, admin, forms, migrations, testing utilities… batteries included.
Spotlight
A high-level web framework for perfectionists with deadlines.
Concept → production, fast.
🔗 docs.djangoproject.com🔌
Auth, sessions, admin, content management, sitemaps, RSS — out of the box.
🧑💻
A full admin UI generated from your models. Manage data with zero extra code.
🔒
Defends against SQL injection, XSS, CSRF, clickjacking by default.
📚
Comprehensive, regularly updated, beginner-friendly — one of the best in open source.
👥
Thousands of plugins (django-rest-framework, channels, allauth…).
⚡
From idea to running app in minutes, not days.
Three commands — you have a running web app:
$ pip install django
$ django-admin startproject mysite
$ cd mysite
$ python manage.py startapp blog
$ python manage.py runserver
# Starting development server at http://127.0.0.1:8000/
Project structure that gets generated:
mysite/ ├── manage.py # CLI entrypoint ├── mysite/ │ ├── settings.py # config │ ├── urls.py # URL routing │ └── wsgi.py # server hook └── blog/ ├── models.py # <-- you ├── views.py # <-- you └── templates/ # <-- you
Define your data as Python classes — Django writes the SQL:
# blog/models.py from django.db import models class Author(models.Model): name = models.CharField(max_length=100) class Book(models.Model): title = models.CharField(max_length=200) author = models.ForeignKey(Author, on_delete=models.CASCADE) published = models.DateField()
Migrations & queries — SQL without writing SQL:
$ python manage.py makemigrations $ python manage.py migrate # In Python: recent = Book.objects.filter(published__year=2026) for book in recent: print(book.title, "—", book.author.name)
urls.py — map URLs to view functions:
from django.urls import path from .views import book_list, book_detail urlpatterns = [ path("", book_list, name="book-list"), path("<int:book_id>/", book_detail, name="book-detail"), ] # mounted at /books/ in the project urls.py
views.py — business logic for each URL:
from django.shortcuts import render from .models import Book def book_list(request): books = Book.objects.all() return render(request, "books.html", {"books": books})
👉 Note: Django calls book_list(request). You never invoke it — that's IoC.
templates/books.html — HTML with placeholders:
<!DOCTYPE html>
<html>
<head><title>Book List</title></head>
<body>
<h1>Books</h1>
<ul>
{% for book in books %}
<li>{{ book.title }} — {{ book.author.name }}</li>
{% empty %}
<li>No books yet.</li>
{% endfor %}
</ul>
</body>
</html>
📚 {{ … }} renders a value. {% … %} runs logic (loops, conditions). HTML is auto-escaped — XSS protection by default.
Alternative Framework
A microframework — small, flexible, opinionated about nothing.
🔗 flask.palletsprojects.comfrom flask import Flask app = Flask(__name__) @app.route("/") def home(): return "Hello, world!" @app.route("/books/<int:book_id>") def book(book_id): return f"Book {book_id}" if __name__ == "__main__": app.run()
from fastapi import FastAPI from pydantic import BaseModel class Book(BaseModel): title: str pages: int app = FastAPI() @app.post("/books") async def create(book: Book): return {"id": 1, **book.dict()} # pydantic validates body automatically # /docs gets you Swagger UI for free
| Django | Flask | FastAPI | |
|---|---|---|---|
| Philosophy | Batteries included | Bring your own batteries | Type-hint driven, async-first |
| Size | Large — full-featured | Tiny core, plugins on top | Lean — built on Starlette + pydantic |
| ORM | Built-in (Django ORM) | None — pick SQLAlchemy | None — SQLAlchemy or SQLModel |
| Admin UI | Auto-generated, free | None — build it yourself | None — auto Swagger/OpenAPI docs |
| Best for | CMS, complex apps, fast MVPs | Small services, prototypes | APIs, microservices, async |
| Learning curve | Steeper at first | Easy — harder later | Easy if you know type hints |
💡 Also worth knowing: Streamlit — data dashboards in pure Python.
Section 3
— somebody on the internet, probably
❌ "Python is too slow."
❌ "Python doesn't scale to millions of API requests."
❌ "Python isn't concurrent / multi-threaded."
❌ "No serious company uses Python at scale — only Java or Go."
Let's look at the receipts.
Heavy Python in data pipelines, recommendations, and ML training.
The streaming backend is Java — but the brains behind Discover Weekly are Python.
Originally written in Python (2005, on mod_python).
Today: Python still drives many internal tools, data pipelines, and ML systems alongside C++/Java for the core video path.
Python since the early days — web search internals, system administration, build tools.
3.5B+ searches/day rely on infrastructure that includes Python.
Server-side data analysis, security automation, risk classification, alerting.
Billions of streamed hours/month — Python in pipelines & ops.
NASA uses Python for:
🛰
From Mars rovers
to your laptop.
📚
when you need a specific capability: charts, HTTP, dates, dataframes.
🏭
when you need structure for an entire application: web app, REST API, ML pipeline.
🔥
A real app uses both: Django + pandas + requests is a normal day.