🐍 Latest Edition
📖 Beginner to Advanced
⏱️ 50 min read
🎯 20+ Sections
⏱️ Estimated reading time: 45-50 minutes
📋 Quick Summary: Python is the #1 fastest-growing programming language, powering everything from web apps (Django, FastAPI) to AI/ML (TensorFlow, PyTorch), data science, automation, and DevOps. By the end of this course, you will go from absolute beginner to writing production-ready Python code. No prior programming experience needed.
🐍 What Is Python?
Python is a high-level, interpreted programming language created by Guido van Rossum in 1991. Its design philosophy emphasizes code readability and simplicity — hence the famous Zen of Python: “Beautiful is better than ugly.”
Python consistently ranks #1-3 on the TIOBE, Stack Overflow, and GitHub Octoverse indexes. It’s the most popular language for first-time learners and the second most demanded language by employers (after JavaScript).
Who Uses Python?
- Google — Core search algorithms, YouTube, Google Cloud
- Netflix — Data pipelines, recommendation engine, content delivery
- Spotify — Backend services, music recommendations, analytics
- NASA — Scientific computing, data analysis for space missions
- Instagram — Backend API (Django), ML for content moderation
- Reddit — Web application backend
💡 Did You Know? Python was named after Monty Python’s Flying Circus, not the snake. The official Python logo is a green snake with two overlapping coils, but the name is a comedy reference. The Zen of Python (PEP 20) includes 19 guiding principles, including “There should be one — and preferably only one — obvious way to do it.”
🤔 Common Myths About Python Debunked
| Myth | Reality |
|---|---|
| “Python is too slow for production” | Python handles millions of requests at Instagram, YouTube, and Spotify. For CPU-bound code, use C extensions or parallel processing. |
| “Python is just for beginners” | Python powers NASA’s Mars rover, CERN’s particle physics, Wall Street trading algorithms, and cutting-edge AI research. |
| “Python can’t do mobile apps” | Kivy, BeeWare, and Chaquopy let you build Android and iOS apps in Python. But for mobile-first, Kotlin/Swift are better. |
| “Python 2 vs 3 is still a problem” | Python 2 has been dead since January 1, 2020. Everything is Python 3 now. Python 3.13+ is the only choice. |
| “You need to install tons of packages” | Python’s standard library is enormous — “batteries included.” JSON, CSV, HTTP, email, threading, SQLite, and more are all built-in. |
🔧 Environment Setup
Let’s get you set up with a professional Python development environment. Same setup used by engineers at Google, Netflix, and Spotify.
Installing Python
# Linux (Ubuntu/Debian) — Python usually pre-installed python3 --version # macOS — use Homebrew brew install python3 # Windows — download from python.org, CHECK "Add Python to PATH" # Or use winget winget install Python.Python.3.13
Choosing an Editor
| Editor | Best For | Rating |
|---|---|---|
| VS Code | Best all-rounder. Python extension by Microsoft | ⭐⭐⭐⭐⭐ |
| PyCharm | Professional IDE, excellent debugging, refactoring | ⭐⭐⭐⭐⭐ |
| Neovim/Vim | Terminal-based, fast, keyboard-driven | ⭐⭐⭐⭐ |
| Thonny | Absolute beginners, built-in debugger | ⭐⭐⭐ |
Virtual Environments
Never install packages globally. Always use virtual environments to isolate dependencies:
# Create a virtual environment python3 -m venv myenv # Activate (Linux/Mac) source myenv/bin/activate # Activate (Windows) myenv\Scripts\activate # Your prompt now shows (myenv) # Install packages inside it pip install requests pandas flask # Deactivate when done deactivate
Your First Python Program
# hello.py
print("Hello, World!")
# Run it
python3 hello.py
# Output: Hello, World!
💡 Did You Know? The
python3command on most systems points to a symlink. You can find where Python is installed withwhich python3(Linux/Mac) orwhere python(Windows). Your virtual environment Python is an isolated copy — it doesn’t affect the system Python.
📝 Python Syntax & Basics
Python’s syntax is designed for readability. No semicolons, no curly braces — just clean indentation.
Variables & Data Types
# Python is dynamically typed — no type declaration needed
name = "Alice" # str
age = 25 # int
height = 5.8 # float
is_student = True # bool
hobbies = ["reading", "coding", "chess"] # list
# Type checking
print(type(name)) # <class 'str'>
print(type(age)) # <class 'int'>
# Type hints (Python 3.5+) — optional but recommended
def greet(name: str) -> str:
return f"Hello, {name}!"
# F-strings (Python 3.6+) — best way to format strings
print(f"{name} is {age} years old and {height}m tall.")
Basic Data Types Reference
| Type | Example | Mutable? | Use Case |
|---|---|---|---|
int |
42 |
No | Whole numbers |
float |
3.14 |
No | Decimal numbers |
str |
"hello" |
No | Text, characters |
list |
[1, 2, 3] |
Yes | Ordered, mutable collections |
tuple |
(1, 2, 3) |
No | Fixed data (coordinates, config) |
dict |
{"a": 1} |
Yes | Key-value mappings |
set |
{1, 2, 3} |
Yes | Unique items, membership tests |
bool |
True / False |
No | Boolean logic |
🔄 Control Flow
Control flow lets your program make decisions and repeat actions. Python uses clean, readable syntax for both.
Conditionals (if/elif/else)
age = 18
if age < 13:
print("Child")
elif age < 20:
print("Teenager")
elif age < 65:
print("Adult")
else:
print("Senior")
# Ternary (one-liner if/else)
status = "Adult" if age >= 18 else "Minor"
# Multiple conditions
if age >= 18 and has_license:
print("Can drive")
# Membership testing
if "python" in ["java", "python", "go"]:
print("Found it!")
# Truthiness
if my_list: # Empty lists are falsy
print("List has items")
if not my_list:
print("List is empty")
Loops (for, while)
# For loop over a range
for i in range(5): # 0, 1, 2, 3, 4
print(i)
# For loop over a list
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit)
# Enumerate — get index AND value
for index, fruit in enumerate(fruits):
print(f"{index}: {fruit}")
# Zip — iterate multiple lists simultaneously
names = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]
for name, score in zip(names, scores):
print(f"{name}: {score}")
# While loop
count = 0
while count < 5:
print(count)
count += 1
# Break and continue
for num in range(10):
if num == 3:
continue # Skip 3
if num == 7:
break # Stop at 7
print(num) # 0, 1, 2, 4, 5, 6
# List comprehension (Pythonic way to create lists)
squares = [x**2 for x in range(10)]
evens = [x for x in range(20) if x % 2 == 0]
# Output: [0, 4, 16, 36, 64]
🧩 Functions & Modules
Functions are reusable blocks of code. Modules let you organize functions into separate files.
Defining Functions
# Basic function
def greet(name):
return f"Hello, {name}!"
# Default parameters
def power(base, exponent=2):
return base ** exponent
print(power(5)) # 25
print(power(5, 3)) # 125
# Keyword arguments
def create_user(name, age, role="user"):
return {"name": name, "age": age, "role": role}
create_user(age=25, name="Alice") # Order doesn't matter
# *args — variable positional arguments
def sum_all(*numbers):
return sum(numbers)
print(sum_all(1, 2, 3, 4, 5)) # 15
# **kwargs — variable keyword arguments
def print_info(**info):
for key, value in info.items():
print(f"{key}: {value}")
print_info(name="Alice", age=25, country="India")
# Lambda (anonymous) functions
square = lambda x: x ** 2
print(square(5)) # 25
# Sort with lambda
students = [("Alice", 85), ("Bob", 92), ("Charlie", 78)]
sorted_students = sorted(students, key=lambda s: s[1], reverse=True)
# Decorators — modify function behavior
def timer(func):
import time
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
print(f"{func.__name__} took {time.time()-start:.3f}s")
return result
return wrapper
@timer
def slow_function():
import time
time.sleep(1)
return "Done"
Modules & Imports
# Import entire module
import math
print(math.pi) # 3.14159...
print(math.sqrt(16)) # 4.0
# Import specific items
from datetime import datetime, timedelta
print(datetime.now())
# Import with alias
import pandas as pd
import numpy as np
# Your own module: utils.py
# def add(a, b): return a + b
from utils import add
print(add(3, 5)) # 8
# __name__ == "__main__" guard
if __name__ == "__main__":
# This code only runs when script is executed directly
main()
📊 Data Structures Deep Dive
Python provides powerful built-in data structures. Mastering them is the key to writing efficient Python code.
Lists (Mutable, Ordered)
nums = [3, 1, 4, 1, 5, 9, 2, 6] # Common operations nums.append(5) # Add to end: [..., 6, 5] nums.insert(0, 99) # Insert at position: [99, 3, ...] nums.remove(1) # Remove first occurrence of 1 popped = nums.pop() # Remove and return last element nums.sort() # In-place sort nums.reverse() # In-place reverse nums.extend([7, 8]) # Merge another list # Slicing nums[0:3] # First 3 elements nums[-1] # Last element nums[::-1] # Reverse copy nums[::2] # Every other element # Copy (important!) copy1 = nums[:] # Shallow copy (preferred) copy2 = nums.copy() # Also shallow copy copy3 = list(nums) # Also works
Dictionaries (Key-Value Store)
user = {
"name": "Alice",
"age": 25,
"skills": ["Python", "SQL", "Docker"]
}
# Access with .get() (safe — no KeyError)
print(user.get("name")) # "Alice"
print(user.get("salary", 0)) # 0 (default)
# Dictionary methods
user.keys() # dict_keys(["name", "age", "skills"])
user.values() # dict_values(["Alice", 25, [...]])
user.items() # dict_items([("name", "Alice"), ...])
user.update({"age": 26}) # Update value
user["email"] = "alice@example.com" # Add new key
# Dictionary comprehension
squares = {x: x**2 for x in range(5)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# Merging (Python 3.9+)
config = {"debug": True, "port": 8000}
override = {"port": 9000, "host": "0.0.0.0"}
merged = config | override # {"debug": True, "port": 9000, "host": "0.0.0.0"}
Sets (Unordered, Unique)
a = {1, 2, 3, 4, 5}
b = {4, 5, 6, 7, 8}
a & b # Intersection: {4, 5}
a | b # Union: {1, 2, 3, 4, 5, 6, 7, 8}
a - b # Difference: {1, 2, 3}
a ^ b # Symmetric difference: {1, 2, 3, 6, 7, 8}
# Set comprehension
evens_under_10 = {x for x in range(10) if x % 2 == 0}
🏗️ Object-Oriented Programming
Python is a fully object-oriented language. Everything in Python is an object.
Classes & Objects
class User:
# Class variable (shared by all instances)
total_users = 0
def __init__(self, username: str, email: str):
"""Constructor — called when object is created"""
self.username = username # Instance variable
self.email = email
self._is_active = True # Protected (convention)
self.__password = None # Private (name mangling)
User.total_users += 1
def __str__(self):
"""String representation for print()"""
return f"User({self.username})"
def __repr__(self):
"""String representation for debugging"""
return f"User('{self.username}', '{self.email}')"
# Instance method
def display_info(self):
print(f"Username: {self.username}, Email: {self.email}")
# Class method
@classmethod
def from_string(cls, data: str):
"""Alternative constructor — parse 'username:email'"""
username, email = data.split(":")
return cls(username, email)
# Static method
@staticmethod
def validate_email(email: str) -> bool:
return "@" in email and "." in email
# Usage
user1 = User("alice", "alice@example.com")
user2 = User.from_string("bob:bob@example.com")
print(User.total_users) # 2
print(User.validate_email("test@test.com")) # True
Inheritance & Polymorphism
class Animal:
def __init__(self, name):
self.name = name
def make_sound(self):
raise NotImplementedError("Subclass must implement")
def __str__(self):
return f"{self.name} (Animal)"
class Dog(Animal):
def make_sound(self):
return "Woof!"
def fetch(self):
return f"{self.name} fetches the ball"
class Cat(Animal):
def make_sound(self):
return "Meow!"
def scratch(self):
return f"{self.name} scratches the couch"
# Polymorphism
animals = [Dog("Rex"), Cat("Whiskers")]
for animal in animals:
print(f"{animal.name} says {animal.make_sound()}")
# Abstract base classes
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius ** 2
Magic Methods (Dunder Methods)
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other): # +
return Vector(self.x + other.x, self.y + other.y)
def __sub__(self, other): # -
return Vector(self.x - other.x, self.y - other.y)
def __mul__(self, scalar): # *
return Vector(self.x * scalar, self.y * scalar)
def __eq__(self, other): # ==
return self.x == other.x and self.y == other.y
def __len__(self): # len()
return int((self.x**2 + self.y**2) ** 0.5)
def __getitem__(self, index): # v[0]
return (self.x, self.y)[index]
def __repr__(self):
return f"Vector({self.x}, {self.y})"
v1 = Vector(3, 4)
v2 = Vector(1, 2)
print(v1 + v2) # Vector(4, 6)
print(len(v1)) # 5
📁 File Handling & I/O
# Reading files — ALWAYS use context manager (with)
with open("data.txt", "r") as file:
content = file.read() # Entire file as string
lines = file.readlines() # List of lines
# Read line by line (memory efficient)
with open("large_file.txt", "r") as file:
for line in file:
print(line.strip())
# Writing files
with open("output.txt", "w") as file:
file.write("Hello, World!\n")
file.writelines(["line1\n", "line2\n", "line3\n"])
# Append mode
with open("log.txt", "a") as file:
file.write(f"Log entry: {datetime.now()}\n")
# Working with JSON
import json
data = {"name": "Alice", "scores": [85, 92, 78]}
# Write JSON
with open("data.json", "w") as f:
json.dump(data, f, indent=2)
# Read JSON
with open("data.json", "r") as f:
loaded = json.load(f)
# CSV files
import csv
with open("users.csv", "r") as f:
reader = csv.DictReader(f)
for row in reader:
print(row["name"], row["email"])
# Path handling (use pathlib, not os.path!)
from pathlib import Path
p = Path("/home/user/documents/file.txt")
print(p.name) # file.txt
print(p.stem) # file
print(p.suffix) # .txt
print(p.parent) # /home/user/documents
print(p.exists()) # True/False
print(p.is_file()) # True/False
# Create directories
Path("output/data").mkdir(parents=True, exist_ok=True)
# List files
for file in Path(".").glob("*.py"):
print(file)
🚨 Error Handling & Exceptions
# Basic try/except
try:
result = 10 / 0
except ZeroDivisionError:
print("Cannot divide by zero!")
# Multiple exception types
try:
num = int(input("Enter a number: "))
result = 100 / num
except ValueError:
print("That's not a valid number!")
except ZeroDivisionError:
print("Can't divide by zero!")
except Exception as e:
print(f"Unexpected error: {e}")
# Try/except/else/finally
try:
file = open("data.txt", "r")
content = file.read()
except FileNotFoundError:
print("File not found")
else:
print(f"Read {len(content)} characters") # Runs if no exception
finally:
file.close() # Always runs — cleanup happens here
# Better: context manager
try:
with open("data.txt", "r") as file:
content = file.read()
except FileNotFoundError:
print("File not found")
# Raising exceptions
def validate_age(age):
if age < 0:
raise ValueError("Age cannot be negative")
if age > 150:
raise ValueError("Age seems unrealistic")
return True
# Custom exceptions
class InsufficientFundsError(Exception):
"""Raised when account balance is insufficient."""
def __init__(self, balance, amount):
self.balance = balance
self.amount = amount
super().__init__(f"Insufficient funds: ${balance} < ${amount}")
def withdraw(balance, amount):
if amount > balance:
raise InsufficientFundsError(balance, amount)
return balance - amount
📦 Libraries & Package Management
Python’s ecosystem is enormous. The Python Package Index (PyPI) hosts over 500,000 packages.
pip — Package Installer
# Common pip commands pip install requests pip install flask==2.3.0 # Specific version pip install "numpy>=1.24, <2.0" # Version range pip install -r requirements.txt # Install from file pip list # List installed packages pip freeze > requirements.txt # Export environment pip uninstall package_name pip show package_name # Package details
Essential Python Libraries
| Library | Category | What It Does |
|---|---|---|
requests |
Web | HTTP requests — the easiest way to call APIs |
pandas |
Data | Data manipulation and analysis (like Excel on steroids) |
numpy |
Math | Numerical computing, arrays, linear algebra |
matplotlib |
Viz | Data visualization — charts, graphs, plots |
fastapi |
Web | Modern async web framework for APIs |
pytest |
Testing | Testing framework — simpler than unittest |
sqlalchemy |
DB | ORM for database interaction |
beautifulsoup4 |
Web | HTML/XML parsing — web scraping |
selenium |
Automation | Browser automation |
tensorflow |
AI/ML | Machine learning and neural networks |
🔌 Working with APIs
import requests
# GET request
response = requests.get("https://api.github.com/users/octocat")
print(response.status_code) # 200
print(response.json()) # Parse JSON response
data = response.json()
print(data["login"]) # "octocat"
# POST request
new_post = {
"title": "Hello API",
"body": "Learning to use APIs with Python",
"userId": 1
}
response = requests.post(
"https://jsonplaceholder.typicode.com/posts",
json=new_post,
headers={"Authorization": "Bearer YOUR_TOKEN"}
)
print(response.status_code) # 201 (Created)
print(response.json()["id"]) # New post ID
# Error handling
try:
response = requests.get("https://api.example.com/data", timeout=10)
response.raise_for_status() # Raises HTTPError for 4xx/5xx
except requests.Timeout:
print("Request timed out")
except requests.HTTPError as e:
print(f"HTTP error: {e}")
except requests.ConnectionError:
print("Could not connect")
# Query parameters
params = {"q": "python", "sort": "stars", "per_page": 10}
response = requests.get("https://api.github.com/search/repositories", params=params)
# File download
response = requests.get("https://example.com/image.jpg", stream=True)
with open("image.jpg", "wb") as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
🧪 Testing in Python
Good code has tests. Python makes testing easy with built-in tools.
# test_utils.py — using pytest
# Install: pip install pytest
import pytest
from utils import add, divide, User
def test_add():
assert add(2, 3) == 5
assert add(-1, 1) == 0
assert add(0, 0) == 0
def test_divide():
assert divide(10, 2) == 5
assert divide(7, 3) == 7 / 3
# Test exception
with pytest.raises(ValueError):
divide(10, 0)
# Parametrized tests
@pytest.mark.parametrize("a, b, expected", [
(1, 2, 3),
(0, 0, 0),
(-1, 1, 0),
(100, 200, 300),
])
def test_add_params(a, b, expected):
assert add(a, b) == expected
# Fixtures — setup/teardown
@pytest.fixture
def sample_user():
return User("testuser", "test@example.com")
def test_user_creation(sample_user):
assert sample_user.username == "testuser"
assert sample_user.email == "test@example.com"
# Run tests:
# pytest # Auto-discover test_*.py
# pytest -v # Verbose
# pytest test_utils.py # Specific file
# pytest -k "add" # Filter by name
# pytest --cov=. # With coverage
⚡ Advanced Python Topics
Generators & Iterators
# Generator — lazy evaluation, memory efficient
def fibonacci(n):
a, b = 0, 1
for _ in range(n):
yield a
a, b = b, a + b
for num in fibonacci(10):
print(num) # 0, 1, 1, 2, 3, 5, 8, 13, 21, 34
# Generator expression (like list comp but lazy)
squares = (x**2 for x in range(1000000)) # No memory allocated
print(next(squares)) # 0
print(next(squares)) # 1
# Generator pipeline
def read_logs(path):
with open(path) as f:
for line in f:
yield line
def filter_errors(lines):
for line in lines:
if "ERROR" in line:
yield line
errors = filter_errors(read_logs("app.log"))
for error in errors:
print(error)
Context Managers & with Statement
# Custom context manager — class-based
class Timer:
def __enter__(self):
import time
self.start = time.time()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
import time
self.duration = time.time() - self.start
print(f"Took {self.duration:.3f}s")
with Timer() as timer:
sum(range(1000000))
# Custom context manager — decorator-based (simpler)
from contextlib import contextmanager
@contextmanager
def temporary_file(filename, content):
with open(filename, "w") as f:
f.write(content)
yield
import os
os.remove(filename)
with temporary_file("temp.txt", "Hello"):
with open("temp.txt") as f:
print(f.read())
# File is deleted after the block
Multithreading & Multiprocessing
# Threading — for I/O-bound tasks
import threading
import time
def download_file(url):
print(f"Downloading {url}...")
time.sleep(2) # Simulate download
print(f"Finished {url}")
urls = ["url1", "url2", "url3", "url4"]
threads = []
for url in urls:
t = threading.Thread(target=download_file, args=(url,))
threads.append(t)
t.start()
for t in threads:
t.join()
print("All downloads complete!")
# Multiprocessing — for CPU-bound tasks
from multiprocessing import Pool
def square(n):
return n * n
with Pool(4) as pool:
results = pool.map(square, range(100))
print(results[:10]) # [0, 1, 4, 9, 16, ...]
# Async/await (Python 3.5+)
import asyncio
async def fetch_data(url):
print(f"Fetching {url}...")
await asyncio.sleep(1) # Simulate async I/O
return f"Data from {url}"
async def main():
tasks = [fetch_data(f"url{i}") for i in range(5)]
results = await asyncio.gather(*tasks)
print(results)
asyncio.run(main())
🏗️ Real-World Project: CLI Task Manager
Let’s build something real — a command-line task manager with file persistence. This is the kind of project you’d actually use daily.
Step 1: Project Structure
todo/ ├── todo.py # Main CLI entry point ├── tasks.py # Task management logic ├── storage.py # File persistence └── requirements.txt
Step 2: Task Model
# tasks.py
from dataclasses import dataclass, field
from datetime import datetime
from typing import Optional
@dataclass
class Task:
title: str
description: str = ""
completed: bool = False
priority: int = 2 # 1=high, 2=medium, 3=low
created_at: str = field(default_factory=lambda: datetime.now().isoformat())
id: Optional[int] = None
Step 3: Storage
# storage.py
import json
from pathlib import Path
from typing import List
from tasks import Task
DATA_FILE = Path.home() / ".todo_data.json"
def load_tasks() -> List[Task]:
if not DATA_FILE.exists():
return []
with open(DATA_FILE) as f:
data = json.load(f)
return [Task(**task) for task in data]
def save_tasks(tasks: List[Task]) -> None:
data = []
for task in tasks:
task_dict = {
"title": task.title,
"description": task.description,
"completed": task.completed,
"priority": task.priority,
"created_at": task.created_at,
"id": task.id,
}
data.append(task_dict)
DATA_FILE.parent.mkdir(parents=True, exist_ok=True)
with open(DATA_FILE, "w") as f:
json.dump(data, f, indent=2)
Step 4: CLI Interface
# todo.py
import argparse
from tasks import Task
from storage import load_tasks, save_tasks
from typing import List
def list_tasks(tasks: List[Task], show_all: bool = False):
if not tasks:
print("📭 No tasks found.")
return
for task in tasks:
if not show_all and task.completed:
continue
status = "✅" if task.completed else "⬜"
priority_mark = {1: "🔴", 2: "🟡", 3: "🟢"}.get(task.priority, "⚪")
print(f"{status} [{priority_mark}] #{task.id}: {task.title}")
if task.description:
print(f" {task.description}")
def add_task(tasks: List[Task], title: str, desc: str, priority: int):
task_id = max((t.id for t in tasks if t.id), default=0) + 1
task = Task(title=title, description=desc, priority=priority, id=task_id)
tasks.append(task)
save_tasks(tasks)
print(f"✅ Task #{task_id} added: {title}")
def complete_task(tasks: List[Task], task_id: int):
for task in tasks:
if task.id == task_id:
task.completed = True
save_tasks(tasks)
print(f"✅ Task #{task_id} marked complete")
return
print(f"❌ Task #{task_id} not found")
def delete_task(tasks: List[Task], task_id: int):
initial = len(tasks)
tasks[:] = [t for t in tasks if t.id != task_id]
if len(tasks) < initial:
save_tasks(tasks)
print(f"🗑️ Task #{task_id} deleted")
else:
print(f"❌ Task #{task_id} not found")
def main():
parser = argparse.ArgumentParser(description="📋 CLI Todo Manager")
parser.add_argument("command", choices=["list", "add", "done", "delete"])
parser.add_argument("args", nargs="*", help="Command arguments")
args = parser.parse_args()
tasks = load_tasks()
if args.command == "list":
show_all = "--all" in args.args
list_tasks(tasks, show_all)
elif args.command == "add":
if len(args.args) < 1:
print("Usage: todo add <title> [description] [--priority N]")
return
title = args.args[0]
desc = args.args[1] if len(args.args) > 1 else ""
priority = 2
if "--priority" in args.args:
idx = args.args.index("--priority")
priority = int(args.args[idx + 1])
add_task(tasks, title, desc, priority)
elif args.command == "done":
if not args.args:
print("Usage: todo done <task_id>")
return
complete_task(tasks, int(args.args[0]))
elif args.command == "delete":
if not args.args:
print("Usage: todo delete <task_id>")
return
delete_task(tasks, int(args.args[0]))
if __name__ == "__main__":
main()
Usage
python3 todo.py add "Learn Python" "Complete the Python course" python3 todo.py add "Build a project" "Build something real" --priority 1 python3 todo.py list python3 todo.py done 1 python3 todo.py delete 2 python3 todo.py list --all
✅ Done! You just built a real CLI application with file persistence, error handling, and a clean interface. This is the same architecture used by professional CLI tools like pip, git, and docker.
❌ Common Mistakes & How to Avoid Them
🔴 Mistake #1: Mutable Default Arguments
What happens: Default arguments are evaluated once at function definition, not on each call. Lists and dicts accumulate across calls.
How to fix: Use None as default and create the mutable inside the function.
# WRONG
def add_item(item, items=[]):
items.append(item)
return items
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] ← items.shared across calls!
# RIGHT
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items
🔴 Mistake #2: Modifying a List While Iterating
What happens: Removing items from a list while looping skips elements and causes indexing errors.
How to fix: Iterate over a copy or use list comprehension.
# WRONG
nums = [1, 2, 3, 4, 5]
for n in nums:
if n % 2 == 0:
nums.remove(n) # Skips elements!
# RIGHT
nums = [n for n in nums if n % 2 != 0]
# Or iterate over a copy
for n in nums[:]:
if n % 2 == 0:
nums.remove(n)
🔴 Mistake #3: Catching Bare Exceptions
What happens: except: catches everything including KeyboardInterrupt and SystemExit — making your program impossible to kill.
How to fix: Always catch specific exceptions.
# WRONG
try:
result = risky_operation()
except:
print("Something went wrong") # Also catches Ctrl+C!
# RIGHT
try:
result = risky_operation()
except ValueError as e:
print(f"Invalid value: {e}")
except OSError as e:
print(f"OS error: {e}")
except Exception as e:
print(f"Unexpected: {e}")
🔴 Mistake #4: Using == for None Checks
What happens: == uses the object’s __eq__ method which could be overridden. is checks identity.
How to fix: Always use is None and is not None.
# WRONG if value == None: # Custom __eq__ could make this return True # RIGHT if value is None: if value is not None:
🔴 Mistake #5: Ignoring Virtual Environments
What happens: Package conflicts when different projects need different versions of the same library.
How to fix: Always use python3 -m venv venv and activate before installing packages.
🔴 Mistake #6: Forgetting to Close Files
What happens: File descriptors leak. At scale, your program crashes with “too many open files.”
How to fix: Always use the with statement.
# WRONG
f = open("data.txt")
data = f.read() # What if exception here? f never closes!
# RIGHT
with open("data.txt") as f:
data = f.read() # Auto-closes even on exceptions
🔴 Mistake #7: Importing Inside Loops
What happens: Imports are cached, so it works — but it’s slow and confusing.
How to fix: Always put imports at the top of the file.
🧠 Test Your Knowledge
Before moving on, check if you really understood the key concepts:
- What does the
withstatement do?
A) Creates a virtual environment
B) Ensures proper resource cleanup (context manager)
C) Imports a module
D) Defines a function
Answer: B — Context managers guarantee setup/cleanup, even on exceptions. - Which data structure is best for key-value lookups?
A) List
B) Tuple
C) Dictionary
D) Set
Answer: C — Dictionaries provide O(1) average-time key lookups. - What’s wrong with
def func(items=[])?
A) Nothing, it’s perfectly fine
B) List is not a valid default
C) Default is mutable and shared across all calls
D) You can’t use lists as parameters
Answer: C — Default arguments are evaluated once at definition time and shared. - Which module would you use to make HTTP requests?
A)os
B)sys
C)requests
D)csv
Answer: C — Therequestslibrary is the standard for HTTP in Python. - What does a generator do?
A) Generates random numbers
B) Lazily produces a sequence of values without storing them all in memory
C) Creates HTML files
D) Manages file uploads
Answer: B — Generators useyieldto produce values on-demand, saving memory.
✅ Scored 4/5 or higher? You’re ready to build real Python applications.
📉 Scored lower? Review the sections above before continuing.
❓ Frequently Asked Questions (FAQ)
Q1: Is Python worth learning in 2026?
Absolutely. Python is the #1 language for AI/ML, data science, automation, and backend development. It has the largest job growth of any programming language. With the rise of AI, Python skills are more valuable than ever. It’s also the best first language to learn programming concepts.
Q2: Python 3.13 vs 3.12 — should I upgrade?
Python 3.13 brings significant performance improvements (up to 30% faster in some benchmarks), improved error messages with better tracebacks, and experimental free-threaded mode (no-GIL). If your dependencies support it, upgrade. Otherwise, 3.12 is stable and widely supported.
Q3: How long does it take to learn Python?
You can learn the basics in 2-3 weeks of consistent practice (1-2 hours/day). Building real projects takes 2-3 months. Mastery (optimization, design patterns, ecosystem) takes 1-2 years. Python’s gentle learning curve means you’ll be productive faster than with most languages.
Q4: Python or JavaScript — which should I learn first?
Learn Python first if you’re interested in data science, AI/ML, automation, or backend. Learn JavaScript first if you want frontend web development. For overall career value, Python has slightly broader applications. But the best answer is: learn both. They complement each other perfectly.
Q5: Can I build mobile apps with Python?
Yes, but it’s not ideal. Frameworks like Kivy, BeeWare, and Chaquopy let you build mobile apps in Python, but for production mobile apps, Kotlin (Android) or Swift (iOS) are better choices. Python excels at the backend that powers mobile apps, not the apps themselves.
Q6: What IDE should I use for Python?
VS Code with the Python extension is the most popular choice — it’s free, fast, and has excellent Python support. PyCharm is better for large projects with its superior refactoring, debugging, and Django support. Both are excellent. Avoid using a basic text editor — you’ll miss linting, autocomplete, and debugging.
Q7: What’s the difference between Python lists and tuples?
Lists are mutable (can be changed after creation) and use []. Tuples are immutable (cannot be changed) and use (). Use lists for homogeneous data that changes (shopping list, user IDs). Use tuples for fixed, heterogeneous data (coordinates, database records).
Q8: How do I handle large datasets in Python?
Use pandas for tabular data (DataFrames), numpy for numerical arrays, and generators for streaming data. For datasets larger than RAM, use Dask (parallel computing), Vaex (out-of-core dataframes), or PySpark (distributed computing). SQL databases also handle large datasets efficiently.
Q9: Is Python good for microservices?
Yes, especially with FastAPI (async, high performance) or Flask/Django. Python microservices are used at Uber, Netflix, and Spotify. For high-throughput services, combine Python with Go or Rust for performance-critical paths. Python excels at the business logic layer.
Q10: How do I contribute to open-source Python projects?
Start with the CPython project itself (the Python implementation). Look for “good first issue” or “help wanted” labels on GitHub. CPython has a devguide (devguide.python.org) that walks you through building and contributing. Other beginner-friendly projects: pandas, pytest, mypy, black, and requests.
📖 Glossary: Key Terms Explained
| Term | Definition |
|---|---|
| PEP | Python Enhancement Proposal — design documents for new features, standards, and processes |
| GIL | Global Interpreter Lock — a mutex that prevents multiple threads from executing Python bytecode simultaneously |
| WSGI | Web Server Gateway Interface — standard interface between Python web apps and servers |
| PyPI | Python Package Index — the official repository of Python packages (500K+ packages) |
| Decorator | A function that takes another function and extends its behavior without modifying it |
| Generator | A function that yields values lazily using yield — memory efficient for large sequences |
| Context Manager | An object that manages resources using the with statement (setup/cleanup) |
| Dunder | “Double underscore” methods like __init__, __str__, __add__ — magic methods for operator overloading |
| Type Hint | Optional annotations that specify expected types in function signatures (def add(a: int, b: int) -> int) |
| Comprehension | Concise syntax for creating sequences: [x**2 for x in range(10)] |
| pip | Python’s package installer — downloads and manages packages from PyPI |
| REPL | Read-Eval-Print Loop — Python’s interactive shell for trying code in real-time |
| Lambda | Anonymous inline function: lambda x: x * 2 — used for short operations |
| Magic Method | See “Dunder” — allows custom classes to use Python operators |
| Virtual Environment | Isolated Python environment with its own packages, preventing dependency conflicts |
✅ Do’s & Don’ts: Quick Reference
| ✅ Do | ❌ Don’t |
|---|---|
| Use virtual environments | Install packages globally |
Use with for file operations |
Open files without closing them |
| Use list comprehensions | Write manual for-loops for simple transformations |
Use .get() for dict access |
Use dict[key] without checking if key exists |
| Catch specific exceptions | Bare except: (catches Ctrl+C too) |
| Write type hints | Leave all code untyped |
Use pathlib.Path |
String concatenation for paths |
| Use f-strings for formatting | %s formatting or .format() |
💡 10 Pro Tips Learned the Hard Way
- Use
pathlibnotos.path. Path objects are cleaner, cross-platform, and chainable.Path("data") / "subdir" / "file.json"works on every OS.os.path.join()is the old way. - Install black and ruff immediately.
pip install black ruff. Black auto-formats your code so you never debate indentation. Ruff lints 500+ rules in microseconds. Both run as pre-commit hooks. - Use
__repr__on every class. It makes debugging infinitely easier. When youprint(my_object), you see useful info instead of<__main__.MyObject object at 0x7f...>. - Learn list comprehensions but don’t overdo them. One-liners are elegant. Nested comprehensions with conditions are unreadable. If it’s complex, use a regular for-loop or extract a function.
- Profile before optimizing. Use
python -m cProfile my_script.pyto find real bottlenecks. 90% of the time you’re optimizing the wrong thing. Measure first, optimize second. - Use
dataclassesfor data containers.@dataclassauto-generates__init__,__repr__,__eq__, and__hash__. Less boilerplate than writing classes manually. - Always use
if __name__ == "__main__":This guard lets your script be both a module (importable) and a standalone script. Without it, importing your file runs the code immediately. - Use
pip freeze > requirements.txtoften. Commit this file to version control. When another dev clones your project,pip install -r requirements.txtsets up the exact same environment. - Learn
pdbor use VS Code/PyCharm debugger.import pdb; pdb.set_trace()drops you into an interactive debugger at that exact line. You can inspect variables, step through code, and fix bugs in minutes. - Read PEP 8 once, then let the auto-formatter handle it. Python’s style guide (PEP 8) is important, but memorizing it is a waste of time. Black and ruff enforce it automatically.
🗺️ Learning Roadmap: From Zero to Production in 7 Days
| Day | Topic | Goal | ⏱️ Time |
|---|---|---|---|
| 1 | Setup & Basics | Install Python, write first script, variables, data types | 60 min |
| 2 | Control Flow & Data Structures | if/else, loops, lists, dicts, sets, list comprehensions | 90 min |
| 3 | Functions & Modules | Define functions, import modules, lambda, decorators | 90 min |
| 4 | OOP & File I/O | Classes, inheritance, file operations, JSON, CSV | 120 min |
| 5 | Error Handling & Libraries | try/except, pip, work with popular libraries | 90 min |
| 6 | APIs & Testing | HTTP requests, JSON APIs, pytest, test-driven development | 120 min |
| 7 | Build & Deploy Your Project | Build the CLI todo app, package it, publish on PyPI or GitHub | 180 min |
🔍 Troubleshooting: Fix Common Problems
| ⚠️ Problem | 🔍 Cause | ✅ Solution |
|---|---|---|
ModuleNotFoundError |
Package not installed or wrong environment | pip install package_name — check venv is active |
IndentationError |
Mixed tabs and spaces | Run black . to auto-format. Use 4 spaces only. |
pip: command not found |
Python not in PATH or pip not installed | python3 -m pip install package_name |
| “command not found” for installed package | Package scripts aren’t in PATH | Use python3 -m package_name or check PATH includes ~/.local/bin |
Permission denied |
Not executable or wrong permissions | chmod +x script.py or python3 script.py |
| List changed unexpectedly | Mutable default argument or shared reference | Use None defaults and copy() for shared lists |
ValueError: not enough values to unpack |
Tuple/list has fewer items than expected | Check the data structure. Use * for extended unpacking. |
💬 What’s Your Experience?
What’s your Python journey so far? What concept clicked for you and what still feels confusing? Drop a comment below — I read and reply to every single one.
Quick questions to start the conversation:
- What was the hardest Python concept for you?
- What’s the coolest thing you’ve built with Python?
- Python 3.13 upgrade — worth it or wait?
- Which section of this course was most helpful?
📌 TL;DR: If You Learn Nothing Else, Learn These 5
- Data Structures — Lists, dicts, sets, and tuples are the foundation. Master them and you master Python.
- Control Flow — if/else, for loops, while loops, and list comprehensions. These are your everyday tools.
- Functions & Modules — Organize code into reusable functions. Import built-in modules. Write your own.
- Error Handling — try/except properly. Specific exceptions only. Never bare
except:. - Virtual Environments — One per project. Always. No exceptions.
python3 -m venv venv.
More Free Courses on TricksPage
- Git & GitHub Course — Master Git from basics to collaboration workflows, CI/CD, and open-source.
- Linux Commands Course — Complete Linux command line mastery.
- Docker & Swarm Course — Containers, Dockerfiles, Compose, Swarm orchestration.
- n8n Automation Course — Workflow automation with 400+ integrations.
- Agentic AI Course — Build AI agents with ReAct patterns, tools, memory.
💭 Final Thoughts
Python is the closest thing to a superpower in modern tech. It’s the language you learn once and use everywhere — web apps, data pipelines, ML models, automation scripts, DevOps tooling, and beyond.
What makes Python special isn’t the syntax, the libraries, or the speed. It’s the community. Pythonistas believe in readable code, clear documentation, and helping each other. The Zen of Python isn’t a joke — it’s how thousands of people build software together, every day.
🔥 Final Word: “The best way to learn Python is to build something that matters to you. Not exercises. Not tutorials. Something real. That’s where the magic happens.”
The best time to start was yesterday. The second best time is now. 🐍
More Free Courses on TricksPage
- Git & GitHub Course — Master Git from basics to collaboration workflows, CI/CD, and open-source.
- Linux Commands Course — Complete Linux command line mastery.
- Docker & Swarm Course — Containers, Dockerfiles, Compose, Swarm orchestration.
- n8n Automation Course — Workflow automation with 400+ integrations.
- Agentic AI Course — Build AI agents with ReAct patterns, tools, memory.
If this course helped you:
- 📌 Bookmark this page for future reference
- 📤 Share it with someone who needs it
- 💬 Leave a comment — I read every one
- ⭐ Follow the blog for more deep courses