Python — 0 to Hero
Python is a high-level, interpreted programming language created by Guido van Rossum and first released in 1991. It emphasizes code readability through significant indentation and a clean syntax that lets developers express concepts in fewer lines than languages like C++ or Java.
Python's design philosophy, summarized in the Zen of Python (PEP 20), includes principles like "Beautiful is better than ugly," "Explicit is better than implicit," and "Simple is better than complex." These principles guide the language's evolution and community practices.
Today, Python is one of the most popular programming languages in the world, powering web applications (Django, Flask), data science (NumPy, Pandas), machine learning (PyTorch, TensorFlow), automation, scripting, and more. This guide takes you from absolute zero to production-level Python.
Every language journey starts with Hello, World. Python makes it trivial:
| 1 | # This is a comment — Python ignores everything after # |
| 2 | print("Hello, World!") # Output: Hello, World! |
| 3 | |
| 4 | # Python 3 is the current version. Check yours: |
| 5 | import sys |
| 6 | print(sys.version) # e.g., 3.12.0 |
info
Python is dynamically typed — you don't declare types explicitly. A variable is created the moment you assign a value to it.
| 1 | # Variables — no type declaration needed |
| 2 | name = "Alice" # str |
| 3 | age = 30 # int |
| 4 | height = 5.8 # float |
| 5 | is_student = True # bool |
| 6 | hobbies = None # NoneType — absence of value |
| 7 | |
| 8 | # Dynamic typing: same variable can hold different types |
| 9 | thing = 42 |
| 10 | print(type(thing)) # <class 'int'> |
| 11 | thing = "now a string" |
| 12 | print(type(thing)) # <class 'str'> |
| 13 | |
| 14 | # Type hints (Python 3.5+) — documentation, NOT enforced |
| 15 | def greet(name: str) -> str: |
| 16 | return f"Hello, {name}" |
| Type | Example | Mutable? |
|---|---|---|
| int | 42, -1, 0 | No |
| float | 3.14, 1e10, float('inf') | No |
| str | "hello", 'world' | No |
| bool | True, False | No |
| list | [1, 2, 3] | Yes |
| tuple | (1, 2, 3) | No |
| dict | {'a': 1, 'b': 2} | Yes |
| set | {1, 2, 3} | Yes |
best practice
Strings are sequences of Unicode characters. Python treats them as immutable sequences with rich methods.
| 1 | # String creation |
| 2 | s1 = "double quotes" |
| 3 | s2 = 'single quotes' |
| 4 | s3 = """multi-line |
| 5 | strings are useful |
| 6 | for docstrings and long text""" |
| 7 | |
| 8 | # f-strings (Python 3.6+) — preferred way to format |
| 9 | name = "Alice" |
| 10 | age = 30 |
| 11 | print(f"{name} is {age} years old") |
| 12 | # → Alice is 30 years old |
| 13 | |
| 14 | # Expressions inside f-strings |
| 15 | print(f"{2 ** 10}") # → 1024 |
| 16 | |
| 17 | # String methods |
| 18 | text = " hello, world! " |
| 19 | print(text.strip()) # → "hello, world!" |
| 20 | print(text.title()) # → " Hello, World! " |
| 21 | print(text.split(",")) # → [" hello", " world! "] |
| 22 | print(",".join(["a", "b"])) # → "a,b" |
| 23 | print(text.upper()) # → " HELLO, WORLD! " |
| 24 | |
| 25 | # Slicing — one of Python's best features |
| 26 | msg = "Python" |
| 27 | print(msg[0]) # → P (first char) |
| 28 | print(msg[-1]) # → n (last char) |
| 29 | print(msg[1:4]) # → yth (indices 1..3) |
| 30 | print(msg[::-1]) # → nohtyP (reversed) |
Python uses indentation (4 spaces is the convention) to define code blocks. No braces, no end keywords — just whitespace.
| 1 | # Conditionals |
| 2 | x = 10 |
| 3 | if x > 0: |
| 4 | print("positive") |
| 5 | elif x == 0: |
| 6 | print("zero") |
| 7 | else: |
| 8 | print("negative") |
| 9 | |
| 10 | # Truthiness — values that evaluate to False: |
| 11 | # False, None, 0, 0.0, "" (empty string), [] (empty list), {} (empty dict) |
| 12 | if not []: |
| 13 | print("empty list is falsy") # this runs |
| 14 | |
| 15 | # Ternary (conditional expression) |
| 16 | status = "adult" if age >= 18 else "minor" |
| 17 | |
| 18 | # For loop — iterate over any iterable |
| 19 | for i in range(5): # 0, 1, 2, 3, 4 |
| 20 | print(i) |
| 21 | |
| 22 | # Enumerate — get index and value |
| 23 | for idx, val in enumerate(["a", "b", "c"]): |
| 24 | print(idx, val) |
| 25 | |
| 26 | # While loop |
| 27 | count = 0 |
| 28 | while count < 3: |
| 29 | print(count) |
| 30 | count += 1 |
| 31 | |
| 32 | # Break, continue, else on loops |
| 33 | for n in range(10): |
| 34 | if n == 3: |
| 35 | continue # skip 3 |
| 36 | if n == 7: |
| 37 | break # stop at 7 |
| 38 | print(n) |
| 39 | else: |
| 40 | print("loop completed without break") # won't run if break hit |
| 41 | |
| 42 | # Match statement (Python 3.10+) — pattern matching |
| 43 | def describe(value): |
| 44 | match value: |
| 45 | case 0: |
| 46 | return "zero" |
| 47 | case int(x) if x > 0: |
| 48 | return f"positive int: {x}" |
| 49 | case [a, *rest]: |
| 50 | return f"list starting with {a}" |
| 51 | case _: |
| 52 | return "something else" |
Lists are ordered, mutable collections. They can hold elements of different types and support indexing, slicing, and rich methods.
| 1 | # Creating lists |
| 2 | nums = [1, 2, 3, 4, 5] |
| 3 | mixed = [1, "hello", 3.14, True] |
| 4 | nested = [[1, 2], [3, 4]] |
| 5 | |
| 6 | # Indexing and slicing |
| 7 | print(nums[0]) # → 1 |
| 8 | print(nums[-1]) # → 5 |
| 9 | print(nums[1:3]) # → [2, 3] |
| 10 | print(nums[::2]) # → [1, 3, 5] |
| 11 | |
| 12 | # Common methods |
| 13 | nums.append(6) # [1, 2, 3, 4, 5, 6] |
| 14 | nums.extend([7, 8]) # [1, 2, 3, 4, 5, 6, 7, 8] |
| 15 | nums.insert(0, 0) # [0, 1, 2, ...] |
| 16 | nums.pop() # removes and returns last element |
| 17 | nums.remove(3) # removes first occurrence of 3 |
| 18 | nums.sort() # in-place sort |
| 19 | nums.reverse() # in-place reverse |
| 20 | |
| 21 | # List comprehensions — Pythonic and fast |
| 22 | squares = [x ** 2 for x in range(10)] |
| 23 | evens = [x for x in range(20) if x % 2 == 0] |
| 24 | matrix = [[i + j for j in range(3)] for i in range(3)] |
Dictionaries store key-value pairs. Keys must be hashable (immutable types like strings, numbers, tuples). As of Python 3.7, dictionaries maintain insertion order.
| 1 | # Creating dicts |
| 2 | user = {"name": "Alice", "age": 30, "active": True} |
| 3 | |
| 4 | # Alternative constructors |
| 5 | dict(name="Bob", age=25) # from keywords |
| 6 | dict([("a", 1), ("b", 2)]) # from pairs |
| 7 | {x: x ** 2 for x in range(5)} # dict comprehension |
| 8 | |
| 9 | # Accessing |
| 10 | print(user["name"]) # → Alice (KeyError if missing) |
| 11 | print(user.get("email")) # → None (safe access) |
| 12 | print(user.get("email", "N/A")) # → N/A with default |
| 13 | |
| 14 | # Modifying |
| 15 | user["email"] = "alice@example.com" |
| 16 | user.update({"age": 31, "city": "NYC"}) |
| 17 | |
| 18 | # Iteration |
| 19 | for key in user: # keys by default |
| 20 | print(key) |
| 21 | for val in user.values(): # values |
| 22 | print(val) |
| 23 | for k, v in user.items(): # both |
| 24 | print(k, v) |
| 25 | |
| 26 | # Merging (Python 3.9+) |
| 27 | merged = {**dict1, **dict2} # spread operator |
| 28 | merged = dict1 | dict2 # pipe operator |
| 29 | |
| 30 | # Default dict |
| 31 | from collections import defaultdict |
| 32 | counts = defaultdict(int) # missing key → 0 |
| 33 | counts["a"] += 1 # works without KeyError |
Functions are first-class objects in Python. They can be assigned, passed as arguments, and returned from other functions.
| 1 | # Basic function |
| 2 | def greet(name: str) -> str: |
| 3 | """Return a greeting. (This is a docstring.)""" |
| 4 | return f"Hello, {name}" |
| 5 | |
| 6 | # Default arguments (evaluated once at definition!) |
| 7 | def power(base, exp=2): |
| 8 | return base ** exp |
| 9 | |
| 10 | # Keyword and positional arguments |
| 11 | def f(a, b, *args, kw1=None, kw2=None, **kwargs): |
| 12 | """a, b: positional required |
| 13 | *args: extra positional (tuple) |
| 14 | kw1, kw2: keyword-only (must be named) |
| 15 | **kwargs: extra keyword (dict)""" |
| 16 | pass |
| 17 | |
| 18 | # Lambda (anonymous function) |
| 19 | square = lambda x: x ** 2 |
| 20 | sorted(pairs, key=lambda x: x[1]) # sort by second element |
| 21 | |
| 22 | # Type annotations with complex types |
| 23 | from typing import List, Optional, Dict, Union, Callable |
| 24 | |
| 25 | def process(items: List[int], callback: Callable[[int], str]) -> List[str]: |
| 26 | return [callback(x) for x in items] |
| 27 | |
| 28 | # Nested functions and closures |
| 29 | def make_multiplier(factor: int): |
| 30 | def multiply(x: int) -> int: |
| 31 | return x * factor |
| 32 | return multiply |
| 33 | |
| 34 | double = make_multiplier(2) |
| 35 | print(double(5)) # → 10 |
Comprehensions provide a concise way to create sequences. They are more readable and faster than manual loops in most cases.
| 1 | # List comprehension |
| 2 | squares = [x ** 2 for x in range(10)] |
| 3 | |
| 4 | # With condition |
| 5 | evens = [x for x in range(20) if x % 2 == 0] |
| 6 | |
| 7 | # Nested loops |
| 8 | pairs = [(x, y) for x in [1, 2] for y in [3, 4]] |
| 9 | # → [(1,3), (1,4), (2,3), (2,4)] |
| 10 | |
| 11 | # Set comprehension |
| 12 | unique = {len(w) for w in ["hi", "hello", "hey", "hi"]} |
| 13 | # → {2, 3, 5} |
| 14 | |
| 15 | # Dict comprehension |
| 16 | square_map = {x: x ** 2 for x in range(5)} |
| 17 | # → {0: 0, 1: 1, 2: 4, 3: 9, 4: 16} |
| 18 | |
| 19 | # Generator expression — lazy, memory-efficient |
| 20 | gen = (x ** 2 for x in range(10_000_000)) |
| 21 | print(next(gen)) # → 0 |
| 22 | print(next(gen)) # → 1 |
| 23 | |
| 24 | # Generator function with yield |
| 25 | def fibonacci(n): |
| 26 | a, b = 0, 1 |
| 27 | for _ in range(n): |
| 28 | yield a |
| 29 | a, b = b, a + b |
| 30 | |
| 31 | list(fibonacci(10)) # → [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] |
Python uses exceptions for error handling. The try-except-finally pattern gives you control over error recovery.
| 1 | # Basic try-except |
| 2 | try: |
| 3 | result = 10 / 0 |
| 4 | except ZeroDivisionError: |
| 5 | print("can't divide by zero") |
| 6 | |
| 7 | # Multiple exception types |
| 8 | try: |
| 9 | value = int(input("enter a number: ")) |
| 10 | result = 100 / value |
| 11 | except ValueError: |
| 12 | print("that's not a number") |
| 13 | except ZeroDivisionError: |
| 14 | print("can't divide by zero") |
| 15 | except Exception as e: |
| 16 | print(f"unexpected error: {e}") |
| 17 | |
| 18 | # Else and Finally |
| 19 | try: |
| 20 | file = open("data.txt") |
| 21 | data = file.read() |
| 22 | except FileNotFoundError: |
| 23 | print("file not found") |
| 24 | else: |
| 25 | print(f"read {len(data)} characters") # runs if no exception |
| 26 | finally: |
| 27 | file.close() # always runs — good for cleanup |
| 28 | |
| 29 | # Custom exceptions |
| 30 | class ValidationError(Exception): |
| 31 | """Raised when data validation fails.""" |
| 32 | pass |
| 33 | |
| 34 | def validate_age(age: int): |
| 35 | if age < 0: |
| 36 | raise ValidationError("age cannot be negative") |
| 37 | |
| 38 | # Context managers (with statement) |
| 39 | with open("file.txt", "r") as f: # auto-closes |
| 40 | content = f.read() |
Python supports full OOP with classes, inheritance, polymorphism, and encapsulation. Everything in Python is an object, including classes themselves.
| 1 | # Class definition |
| 2 | class Dog: |
| 3 | # Class variable (shared by all instances) |
| 4 | species = "Canis familiaris" |
| 5 | |
| 6 | # Constructor — called when instance created |
| 7 | def __init__(self, name: str, age: int): |
| 8 | self.name = name # instance variable |
| 9 | self.age = age |
| 10 | |
| 11 | # Instance method |
| 12 | def bark(self) -> str: |
| 13 | return f"{self.name} says Woof!" |
| 14 | |
| 15 | # String representation (used by print()) |
| 16 | def __str__(self) -> str: |
| 17 | return f"{self.name} ({self.age} years old)" |
| 18 | |
| 19 | # Official representation (used by repr()) |
| 20 | def __repr__(self) -> str: |
| 21 | return f"Dog('{self.name}', {self.age})" |
| 22 | |
| 23 | # Creating instances |
| 24 | rex = Dog("Rex", 3) |
| 25 | print(rex.bark()) # → Rex says Woof! |
| 26 | print(rex) # → Rex (3 years old) |
| 27 | |
| 28 | # Inheritance |
| 29 | class Puppy(Dog): |
| 30 | def __init__(self, name: str, age: int, toy: str = "ball"): |
| 31 | super().__init__(name, age) # call parent constructor |
| 32 | self.toy = toy |
| 33 | |
| 34 | # Override parent method |
| 35 | def bark(self) -> str: |
| 36 | return f"{self.name} yips!" |
| 37 | |
| 38 | # Duck typing — "if it walks like a duck..." |
| 39 | class Cat: |
| 40 | def bark(self) -> str: |
| 41 | return "Cat goes... bark?" |
| 42 | |
| 43 | def make_it_bark(animal): |
| 44 | print(animal.bark()) |
| 45 | |
| 46 | make_it_bark(rex) # → Rex says Woof! |
| 47 | make_it_bark(Cat()) # → Cat goes... bark? |
| 48 | |
| 49 | # Property decorator |
| 50 | class Temperature: |
| 51 | def __init__(self, celsius: float): |
| 52 | self._celsius = celsius |
| 53 | |
| 54 | @property |
| 55 | def fahrenheit(self) -> float: |
| 56 | return self._celsius * 9 / 5 + 32 |
| 57 | |
| 58 | @fahrenheit.setter |
| 59 | def fahrenheit(self, value: float): |
| 60 | self._celsius = (value - 32) * 5 / 9 |
Python's module system lets you organize code into reusable files. Any .py file is a module, and directories with __init__.py become packages.
| 1 | # Importing modules |
| 2 | import math |
| 3 | from pathlib import Path |
| 4 | from collections import defaultdict, Counter |
| 5 | from typing import Optional as Opt |
| 6 | |
| 7 | # Module structure |
| 8 | # mypackage/ |
| 9 | # __init__.py # makes it a package |
| 10 | # module_a.py |
| 11 | # subpackage/ |
| 12 | # __init__.py |
| 13 | # module_b.py |
| 14 | |
| 15 | # __init__.py can control what gets exported |
| 16 | # __all__ = ["func1", "ClassA"] # restricts `from pkg import *` |
| 17 | |
| 18 | # The if __name__ guard — prevents code from running on import |
| 19 | def main(): |
| 20 | print("Running as script") |
| 21 | |
| 22 | if __name__ == "__main__": |
| 23 | main() |
| 24 | |
| 25 | # Standard library highlights everyone should know: |
| 26 | import os # operating system interface |
| 27 | import sys # Python interpreter access |
| 28 | import json # JSON parsing |
| 29 | import re # regular expressions |
| 30 | import datetime # date and time handling |
| 31 | import itertools # iterator tools |
| 32 | import functools # higher-order functions |
| 33 | import pathlib # modern filesystem paths |
| 34 | import collections # specialized containers |
| 35 | import dataclasses # data classes (3.7+) |
Decorators are functions that take another function and extend its behavior without modifying it directly. They are a powerful metaprogramming tool.
| 1 | # Basic decorator pattern |
| 2 | from functools import wraps |
| 3 | |
| 4 | def timer(func): |
| 5 | """Print how long a function takes to run.""" |
| 6 | @wraps(func) # preserves func's metadata |
| 7 | def wrapper(*args, **kwargs): |
| 8 | import time |
| 9 | start = time.perf_counter() |
| 10 | result = func(*args, **kwargs) |
| 11 | elapsed = time.perf_counter() - start |
| 12 | print(f"{func.__name__} took {elapsed:.4f}s") |
| 13 | return result |
| 14 | return wrapper |
| 15 | |
| 16 | @timer |
| 17 | def slow_function(): |
| 18 | sum(range(10_000_000)) |
| 19 | |
| 20 | slow_function() # → slow_function took 0.2345s |
| 21 | |
| 22 | # Decorators with arguments |
| 23 | def repeat(n: int): |
| 24 | def decorator(func): |
| 25 | @wraps(func) |
| 26 | def wrapper(*args, **kwargs): |
| 27 | for _ in range(n): |
| 28 | func(*args, **kwargs) |
| 29 | return wrapper |
| 30 | return decorator |
| 31 | |
| 32 | @repeat(3) |
| 33 | def say_hi(): |
| 34 | print("hi") |
| 35 | |
| 36 | say_hi() # prints "hi" three times |
| 37 | |
| 38 | # Built-in decorators |
| 39 | @staticmethod # method that doesn't receive self |
| 40 | @classmethod # method that receives cls instead of self |
| 41 | @property # method accessed like an attribute |
| 42 | @dataclass # auto-generates __init__, __repr__, etc. |
Python offers multiple concurrency models. The Global Interpreter Lock (GIL) affects threading, but asyncio and multiprocessing bypass it for different use cases.
| 1 | # Threading — I/O-bound tasks (GIL-limited) |
| 2 | import threading |
| 3 | import time |
| 4 | |
| 5 | def worker(name: str, delay: float): |
| 6 | time.sleep(delay) |
| 7 | print(f"{name} done") |
| 8 | |
| 9 | threads = [] |
| 10 | for i in range(3): |
| 11 | t = threading.Thread(target=worker, args=(f"T{i}", i)) |
| 12 | threads.append(t) |
| 13 | t.start() |
| 14 | for t in threads: |
| 15 | t.join() # wait for all to finish |
| 16 | |
| 17 | # Asyncio — cooperative concurrency (Python 3.5+) |
| 18 | import asyncio |
| 19 | |
| 20 | async def fetch_data(url: str) -> str: |
| 21 | await asyncio.sleep(1) # simulate I/O |
| 22 | return f"data from {url}" |
| 23 | |
| 24 | async def main(): |
| 25 | tasks = [fetch_data(f"url_{i}") for i in range(5)] |
| 26 | results = await asyncio.gather(*tasks) |
| 27 | print(results) |
| 28 | |
| 29 | asyncio.run(main()) |
| 30 | |
| 31 | # Multiprocessing — CPU-bound tasks (bypasses GIL) |
| 32 | from multiprocessing import Pool |
| 33 | |
| 34 | def expensive(n: int) -> int: |
| 35 | return sum(i * i for i in range(n)) |
| 36 | |
| 37 | with Pool(processes=4) as pool: |
| 38 | results = pool.map(expensive, [10_000, 20_000, 30_000]) |
| 39 | |
| 40 | # Concurrent futures (high-level API) |
| 41 | from concurrent.futures import ThreadPoolExecutor |
| 42 | |
| 43 | with ThreadPoolExecutor(max_workers=4) as executor: |
| 44 | futures = [executor.submit(worker, f"W{i}", i) for i in range(3)] |
| 45 | for f in futures: |
| 46 | f.result() |
Python has built-in testing frameworks and a rich ecosystem of testing tools.
| 1 | # unittest — built-in |
| 2 | import unittest |
| 3 | |
| 4 | def add(a: int, b: int) -> int: |
| 5 | return a + b |
| 6 | |
| 7 | class TestMath(unittest.TestCase): |
| 8 | def test_add(self): |
| 9 | self.assertEqual(add(2, 3), 5) |
| 10 | |
| 11 | def test_add_negative(self): |
| 12 | self.assertEqual(add(-1, 1), 0) |
| 13 | |
| 14 | if __name__ == "__main__": |
| 15 | unittest.main() |
| 16 | |
| 17 | # pytest — third-party, cleaner syntax (pip install pytest) |
| 18 | # test_math.py: |
| 19 | # def test_add(): |
| 20 | # assert add(2, 3) == 5 |
| 21 | # |
| 22 | # def test_add_negative(): |
| 23 | # assert add(-1, 1) == 0 |
| 24 | # |
| 25 | # Run: pytest test_math.py -v |
| 26 | |
| 27 | # Fixtures in pytest |
| 28 | # @pytest.fixture |
| 29 | # def db_connection(): |
| 30 | # conn = create_connection() |
| 31 | # yield conn |
| 32 | # conn.close() |
| 33 | # |
| 34 | # def test_query(db_connection): |
| 35 | # result = db_connection.query("SELECT 1") |
| 36 | # assert result == 1 |
| 37 | |
| 38 | # Mocking |
| 39 | from unittest.mock import Mock, patch |
| 40 | |
| 41 | # Mock an external API call |
| 42 | def get_user_name(api, user_id): |
| 43 | return api.fetch(user_id)["name"] |
| 44 | |
| 45 | def test_get_user_name(): |
| 46 | mock_api = Mock() |
| 47 | mock_api.fetch.return_value = {"name": "Alice"} |
| 48 | assert get_user_name(mock_api, 1) == "Alice" |
The with statement (context manager) ensures proper resource cleanup, even if exceptions occur.
| 1 | # Reading files |
| 2 | with open("example.txt", "r") as f: |
| 3 | content = f.read() # entire file as string |
| 4 | lines = f.readlines() # list of lines |
| 5 | for line in f: # lazy iteration (memory efficient) |
| 6 | print(line.strip()) |
| 7 | |
| 8 | # Writing files |
| 9 | with open("output.txt", "w") as f: |
| 10 | f.write("Hello, World!\n") |
| 11 | f.writelines(["line1\n", "line2\n"]) |
| 12 | |
| 13 | # Binary mode |
| 14 | with open("image.jpg", "rb") as f: |
| 15 | data = f.read() |
| 16 | |
| 17 | # Pathlib — modern path handling (Python 3.4+) |
| 18 | from pathlib import Path |
| 19 | |
| 20 | p = Path("/tmp/data/file.txt") |
| 21 | print(p.name) # → file.txt |
| 22 | print(p.stem) # → file |
| 23 | print(p.suffix) # → .txt |
| 24 | print(p.parent) # → /tmp/data |
| 25 | print(p.exists()) # → True/False |
| 26 | |
| 27 | # Read/write with pathlib |
| 28 | Path("hello.txt").write_text("Hello") |
| 29 | text = Path("hello.txt").read_text() |
| 30 | |
| 31 | # Custom context manager |
| 32 | class ManagedFile: |
| 33 | def __init__(self, name: str): |
| 34 | self.name = name |
| 35 | |
| 36 | def __enter__(self): |
| 37 | self.file = open(self.name, "w") |
| 38 | return self.file |
| 39 | |
| 40 | def __exit__(self, exc_type, exc_val, exc_tb): |
| 41 | self.file.close() |
| 42 | |
| 43 | # Or use contextlib for simpler cases |
| 44 | from contextlib import contextmanager |
| 45 | |
| 46 | @contextmanager |
| 47 | def open_file(name: str): |
| 48 | f = open(name, "w") |
| 49 | try: |
| 50 | yield f |
| 51 | finally: |
| 52 | f.close() |
These patterns separate beginner Python from professional Python. Mastering them will make your code cleaner, faster, and more Pythonic.
| 1 | # Data Classes (Python 3.7+) |
| 2 | from dataclasses import dataclass, field |
| 3 | |
| 4 | @dataclass(order=True) |
| 5 | class Point: |
| 6 | x: float |
| 7 | y: float |
| 8 | label: str = field(default="", compare=False) |
| 9 | |
| 10 | p1 = Point(1.0, 2.0) |
| 11 | p2 = Point(3.0, 4.0) |
| 12 | print(p1) # → Point(x=1.0, y=2.0, label='') |
| 13 | print(p1 < p2) # → True (compares x first) |
| 14 | |
| 15 | # Enums |
| 16 | from enum import Enum, auto |
| 17 | |
| 18 | class Color(Enum): |
| 19 | RED = auto() |
| 20 | GREEN = auto() |
| 21 | BLUE = auto() |
| 22 | |
| 23 | print(Color.RED.name) # → RED |
| 24 | print(Color.RED.value) # → 1 |
| 25 | |
| 26 | # TypeAlias (Python 3.10+) |
| 27 | from typing import TypeAlias |
| 28 | |
| 29 | Vector: TypeAlias = list[float] |
| 30 | Matrix: TypeAlias = list[Vector] |
| 31 | |
| 32 | # Structural pattern matching (3.10+) — advanced |
| 33 | def process_command(cmd: str) -> str: |
| 34 | match cmd.split(): |
| 35 | case ["quit"]: |
| 36 | return "Goodbye" |
| 37 | case ["load", filename]: |
| 38 | return f"Loading {filename}" |
| 39 | case ["save", *rest] if rest: |
| 40 | return f"Saving {rest[0]}" |
| 41 | case _: |
| 42 | return "Unknown command" |
| 43 | |
| 44 | # Walrus operator (3.8+) — assignment expression |
| 45 | if (n := len(items)) > 10: |
| 46 | print(f"Large list: {n} items") |
| 47 | |
| 48 | # ZoneInfo — timezone support (3.9+) |
| 49 | from zoneinfo import ZoneInfo |
| 50 | from datetime import datetime |
| 51 | nyc = datetime.now(ZoneInfo("America/New_York")) |
| 52 | |
| 53 | # Generic types (3.12+) — concise generics |
| 54 | def first[T](items: list[T]) -> T | None: |
| 55 | return items[0] if items else None |
Python's power comes from its ecosystem. These tools and libraries are essential for modern Python development.
| Category | Tools / Libraries | Purpose |
|---|---|---|
| Package Mgmt | pip, uv, poetry, rye | Install and manage dependencies |
| Environment | venv, conda, pyenv | Isolate project dependencies |
| Linting | ruff, flake8, pylint | Catch errors and enforce style |
| Formatting | ruff format, black | Auto-format code |
| Type Checking | mypy, pyright, pyre | Static type analysis |
| Testing | pytest, unittest, tox | Test runner and automation |
| Web | FastAPI, Django, Flask | Web frameworks |
| Data | NumPy, Pandas, Polars | Data manipulation |
| ML/AI | PyTorch, scikit-learn, transformers | Machine learning |
best practice
Tim Peters' Zen of Python (PEP 20) captures Python's design philosophy. Type import this in any Python interpreter to see it.
| 1 | Beautiful is better than ugly. |
| 2 | Explicit is better than implicit. |
| 3 | Simple is better than complex. |
| 4 | Complex is better than complicated. |
| 5 | Flat is better than nested. |
| 6 | Sparse is better than dense. |
| 7 | Readability counts. |
| 8 | Special cases aren't special enough to break the rules. |
| 9 | Although practicality beats purity. |
| 10 | Errors should never pass silently. |
| 11 | Unless explicitly silenced. |
| 12 | In the face of ambiguity, refuse the temptation to guess. |
| 13 | There should be one — and preferably only one — obvious way to do it. |
| 14 | Although that way may not be obvious at first unless you're Dutch. |
| 15 | Now is better than never. |
| 16 | Although never is often better than *right* now. |
| 17 | If the implementation is hard to explain, it's a bad idea. |
| 18 | If the implementation is easy to explain, it may be a good idea. |
| 19 | Namespaces are one honking great idea — let's do more of those! |
Community
Get help on Slack, Discord or VIP
Stuck on a guide? Join the community and ask.