2026-03-24

python

A beginner-friendly guide to Python - covering all core topics in a clear, readable style.

#work-in-progress#python#notes#long
model: claude-sonnet-4-6 human: nmcgi

This guide covers all the core topics you'll find on the official Python documentation, written in a clear, beginner-friendly style. Whether you're brand new to Python or brushing up, use it as a quick reference before diving into projects.

1. Introduction

  • What is Python? A high-level, interpreted, dynamically typed language created by Guido van Rossum (1991). It emphasizes readability and simplicity — Python code often reads almost like plain English.
  • Why learn Python?
    • Clean, minimal syntax → fast to write and easy to read.
    • Massive ecosystem: data science, web, automation, AI/ML.
    • Batteries included — rich standard library out of the box, so you can do a lot without installing anything extra.
  • How to run Python?

2. Program Structure

After creating a new file called hello.py with the following contents:

# hello.py

def main():
    print("Hello, Python!")

if __name__ == "__main__":
    main()

You can then run it in the terminal! With the following effects:

  • print() outputs text to the screen — it's your best friend when learning!
  • def main(): defines a function (a reusable block of code). More on functions in section 13.
  • The if __name__ == "__main__" line is a safety guard. It means: "only run main() if this file is being run directly, not if it's being imported by another file." Don't worry too much about this for now — just know it's a good habit.
  • Indentation (4 spaces) defines blocks — Python uses whitespace instead of { } braces.
  • Each .py file is called a module, and you can import code from one file into another.

3. Syntax Basics

ElementExampleNotes
Comments# this is a commentSingle-line; starts with #. Ignored by Python — just for humans.
Docstrings"""triple quoted"""Multi-line strings used to document functions and classes.
Identifiersmy_var, _privateVariable names use snake_case by convention; case-sensitive (ageAge).
Newline(end of line)Ends a statement. Unlike many languages, no semicolons needed.

4. Variables & Data Types

A variable is just a named container for a value. In Python, you don't need to declare a type — Python figures it out automatically.

age = 25          # int
price = 9.99      # float
name = "Alice"    # str
is_happy = True   # bool
nothing = None    # NoneType

"Mutable" vs "immutable" — mutable means you can change it after creating it; immutable means you can't.

TypeExampleNotes
int42, -7, 0xFFWhole numbers. Python handles numbers of any size.
float3.14, 1e-3Decimal numbers (1e-3 means 0.001).
complex3 + 4jNumbers with a real and imaginary part. Rarely needed as a beginner.
boolTrue, FalseEither true or false. Note the capital letters!
str"hello", 'world'Text. Immutable — you can't change individual characters.
bytesb"data"Raw binary data. You'll encounter this when dealing with files or networking.
list[1, 2, 3]An ordered collection. Mutable — you can add, remove, or change items.
tuple(1, 2, 3)Like a list, but immutable. Good for data that shouldn't change.
set{1, 2, 3}An unordered collection with no duplicates. Mutable.
dict{"a": 1}Key-value pairs (like a real dictionary: word → definition). Mutable.
NoneTypeNoneRepresents "nothing" or the absence of a value. Like null in other languages.

Tip: Use type(x) to check what type a variable is. For example, type(42) returns <class 'int'>. Use isinstance(x, int) to check if x is an integer.

5. Operators

CategorySymbolsExample
Arithmetic+ - * / // % **7 // 23, 2 ** 8256
Assignment= += -= *= /= //= %= **=x += 1 is shorthand for x = x + 1
Comparison== != < > <= >=a == b (note: two equals signs to compare, one to assign)
Identityis is notx is None (checks if two variables point to the same object)
Membershipin not in"a" in "cat"True
Logicaland or notx > 0 and x < 10
Bitwise& | ^ ~ << >>mask & 0xFF (manipulates individual bits — advanced topic)

Note: / always returns a float (e.g., 4 / 22.0). // is integer (floor) division — it rounds down. So 5 // 22, not 2.5. % is the modulo operator: it gives you the remainder (7 % 31).

6. Booleans

A boolean is simply True or False. It's the foundation of all decision-making in code.

flag = True
if flag:
    print("yes")
else:
    print("no")
  • Falsy values are things Python treats as False in a condition: False, None, 0, 0.0, "" (empty string), [] (empty list), {} (empty dict), set() (empty set).
  • Everything else is truthy (treated as True).
  • and / or use short-circuit evaluation: Python stops evaluating as soon as the result is certain. For example, False and <anything> immediately returns False without checking the second part.

7. Conditional Statements

Conditionals let your program make decisions.

if / elif / else

if score >= 90:
    print("A")
elif score >= 80:
    print("B")
else:
    print("C or below")
  • Python checks each condition from top to bottom and runs the first block that matches.
  • elif is short for "else if". You can have as many elif blocks as you need.
  • Ternary expression (a one-liner if/else): grade = "pass" if score >= 60 else "fail"

match (Python 3.10+)

match command:
    case "quit":
        quit()
    case "go" | "move":
        move()
    case _:           # _ is a wildcard — matches anything
        print("unknown command")
  • Think of match as a cleaner way to write many if/elif checks against the same variable.
  • Structural pattern matching — more powerful than a simple switch in other languages.

8. Loops

Loops repeat a block of code. There are two kinds:

LoopSyntaxWhen to use
whilewhile cond:Repeat as long as a condition is true.
forfor x in iterable:Repeat once for each item in a collection.
# Print numbers 1 through 5
for i in range(1, 6):
    print(i)

# Count down from 10 to 1
n = 10
while n > 0:
    n -= 1

Tip: range(start, stop) generates numbers from start up to (but not including) stop. So range(1, 6) gives 1, 2, 3, 4, 5.

  • break — exit the loop immediately, even if the condition is still true.
  • continue — skip the rest of this iteration and jump to the next one.
  • else clause on a loop runs when the loop finishes normally (i.e., no break was hit).

9. Lists & Tuples

A list is an ordered, changeable collection of items. Items can be of any type.

nums = [1, 2, 3, 4, 5]
first = nums[0]       # 1  — indexing starts at 0!
last  = nums[-1]      # 5  — negative index counts from the end
sub   = nums[1:3]     # [2, 3] — slicing: from index 1 up to (not including) 3
  • List comprehension — a compact way to build a list: [x * 2 for x in nums if x > 2][6, 8, 10]
  • Tuples: point = (3, 4) — immutable lists. Use them for data that shouldn't change, like coordinates. Unpack with x, y = point.
# Common list operations
nums.append(6)           # add one item to the end
nums.extend([7, 8])      # add multiple items to the end
nums.sort()              # sort in place (modifies the list)
doubled = list(map(lambda x: x * 2, nums))     # double every item
evens   = list(filter(lambda x: x % 2 == 0, nums))  # keep only even items
total   = sum(nums)      # add everything up

10. Dictionaries & Sets

A dictionary (dict) stores key-value pairs — like a lookup table or a real dictionary (word → definition).

person = {"name": "Alice", "age": 30}
person["city"] = "NYC"           # add a new key
age = person.get("age", 0)       # safe access: returns 0 if "age" doesn't exist

# Loop over all key-value pairs
for key, val in person.items():
    print(key, val)
# Dict comprehension — build a dict in one line
squares = {x: x**2 for x in range(1, 6)}
# → {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

# Sets — unordered collections with no duplicates
a = {1, 2, 3}
b = {2, 3, 4}
print(a | b)   # union (all items from both)     → {1, 2, 3, 4}
print(a & b)   # intersection (items in both)    → {2, 3}
print(a - b)   # difference (in a but not in b)  → {1}

11. Strings

Strings are sequences of characters. They're immutable — you can't change a character in place, but you can create new strings from operations.

s = "Hello, World!"
print(len(s))                         # 13 — number of characters
print(s.upper())                      # "HELLO, WORLD!"
print(s.replace("World", "Python"))   # "Hello, Python!"
print(s.split(", "))                  # ["Hello", "World!"] — splits on ", "
print(s[0:5])                         # "Hello" — slicing works like lists
  • f-strings (Python 3.6+): the easiest way to embed variables in a string: f"Name: {name}, Age: {age}". The f prefix tells Python to evaluate the {} expressions.
  • Multi-line strings: wrap in """...""" or '''...'''.

12. User Input

name = input("Enter your name: ")
age  = int(input("Enter your age: "))   # convert the string to an integer
print(f"Hello, {name}! You are {age} years old.")
  • input() always returns a string, even if the user types a number. You need to explicitly convert it: int("42")42, float("3.14")3.14.

13. Functions

A function is a reusable block of code. Instead of copy-pasting the same logic, you define it once and call it whenever you need it.

Definition

def add(a, b):
    return a + b

result = add(3, 5)   # result = 8

Default & Keyword Arguments

def greet(name, greeting="Hello"):   # "Hello" is the default if no greeting is given
    print(f"{greeting}, {name}!")

greet("Alice")                   # Hello, Alice!
greet("Bob", greeting="Hi")      # Hi, Bob! — passing by keyword name

*args and **kwargs

These let a function accept any number of arguments:

def variadic(*args, **kwargs):
    print(args)    # a tuple of all positional arguments: (1, 2, 3)
    print(kwargs)  # a dict of all keyword arguments: {"color": "red"}

variadic(1, 2, 3, color="red")
  • *args catches extra positional arguments (passed by position).
  • **kwargs catches extra keyword arguments (passed by name).

Lambda Functions

A lambda is a short, anonymous (unnamed) function written in one line. Best for simple operations:

square = lambda x: x ** 2
result = square(5)   # 25

# Often used with map/filter/sort:
nums = [3, 1, 4, 1, 5]
nums.sort(key=lambda x: -x)   # sort in descending order

Scope (where variables live)

ScopeNotes
LocalVariables created inside a function. They disappear when the function returns.
EnclosingVariables from an outer function, accessible in nested (inner) functions. Use nonlocal to modify them.
GlobalVariables defined at the top of a file. Use global inside a function to modify them.
Built-inPython's built-in names like len, print, range. Always available.

14. Comprehensions

Comprehensions are a concise, Pythonic way to build collections. Think of them as a one-line for loop that produces a value.

# List comprehension — [expression for item in iterable if condition]
squares = [x**2 for x in range(10)]
# same as: squares = []; for x in range(10): squares.append(x**2)

# Dict comprehension
inverted = {v: k for k, v in mapping.items()}

# Set comprehension
unique_lens = {len(w) for w in words}

# Generator expression (lazy — computes values on demand, uses less memory)
total = sum(x**2 for x in range(1_000_000))

Tip: If the result is just going to be consumed once (like in sum()), use a generator expression (no square brackets). If you need to store and reuse the list, use a list comprehension.

15. Iterators & Generators

A generator is a function that produces a sequence of values one at a time, instead of building the whole list in memory at once. It uses the yield keyword.

def countdown(n):
    while n > 0:
        yield n   # pause here, send n to the caller, then resume next time
        n -= 1

for num in countdown(5):
    print(num)   # prints 5, 4, 3, 2, 1
  • Think of yield like a pause button: the function pauses, hands a value to the caller, then resumes from that exact point next time.
  • Generators are memory-efficient — great for processing large files or infinite sequences.
  • Use next(gen) to manually get the next value from a generator or iterator.

16. Classes & Objects

A class is a blueprint for creating objects. An object is an instance of a class — for example, alice is an object (instance) of the Person class.

class Person:
    species = "Homo sapiens"   # class attribute — shared by ALL Person objects

    def __init__(self, name, age):
        # __init__ runs automatically when you create a new Person
        self.name = name   # instance attribute — unique to each object
        self.age  = age

    def greet(self):
        # self refers to the specific object calling this method
        return f"Hi, I'm {self.name}."

    def __repr__(self):
        # How this object looks when printed or inspected
        return f"Person({self.name!r}, {self.age})"

alice = Person("Alice", 30)   # create a Person object
print(alice.greet())           # Hi, I'm Alice.

Inheritance

Inheritance lets one class reuse and extend another class's code:

class Employee(Person):   # Employee inherits from Person
    def __init__(self, name, age, role):
        super().__init__(name, age)   # call Person's __init__ to set name & age
        self.role = role

    def greet(self):
        # Override Person's greet method
        return f"{super().greet()} I work as {self.role}."
  • Python supports multiple inheritance (inheriting from more than one class). The order Python uses to look up methods is called the MRO (Method Resolution Order).
  • Dunder methods (double underscore, like __init__, __repr__, __len__) are special methods that customize how your objects behave with built-in Python operations.

17. Error Handling

Errors in Python are called exceptions. Instead of crashing, you can catch them and handle them gracefully.

try:
    result = int(input("Enter a number: "))
except ValueError as e:
    # Runs if the user types something that isn't a number
    print(f"Bad input: {e}")
except (TypeError, ZeroDivisionError):
    # You can catch multiple exception types in one line
    print("Type or division error")
else:
    # Runs only if NO exception was raised
    print(f"Got {result}")
finally:
    # ALWAYS runs, whether an exception happened or not — great for cleanup
    print("Done")
  • raise ValueError("msg") — throw an exception yourself (e.g., to signal bad input).
  • raise alone — re-raises the current exception (pass it up the call stack).
  • Define custom exceptions by subclassing Exception: class MyError(Exception): pass.
TechniqueDescription
try/exceptCatch specific or broad exceptions.
else clauseRuns when no exception was raised in the try block.
finally clauseCleanup that always executes (close files, release locks, etc.).
Context managers (with)Automatic resource management — see section 18.

18. Context Managers

A context manager (used with with) automatically handles setup and cleanup for you — like ensuring a file is closed even if an error occurs.

with open("data.txt", "r") as f:
    content = f.read()
# The file is automatically closed here, no matter what
  • Without with, you'd need to manually call f.close() and worry about doing it in error cases.
  • Under the hood, with calls __enter__ when entering the block and __exit__ when leaving (even on exception).
  • You can create your own context managers using contextlib.contextmanager or by adding __enter__/__exit__ methods to a class.

19. File I/O

ModeMeaning
"r"Read (text, default). Fails if the file doesn't exist.
"w"Write (overwrites / creates the file).
"a"Append (adds to the end without erasing).
"rb" / "wb"Binary read / write (for images, PDFs, etc.).
# Write to a file
with open("out.txt", "w") as f:
    f.write("Hello\n")

# Read line by line (memory-efficient for large files)
with open("out.txt") as f:
    for line in f:
        print(line.strip())   # .strip() removes the trailing newline

# Read the whole file into a string at once
with open("out.txt") as f:
    text = f.read()

20. Modules & Packages

A module is any .py file. A package is a folder of modules with an __init__.py file. You import code to reuse it:

import math
from os import path, getcwd
from collections import defaultdict, Counter
import json

print(math.sqrt(16))            # 4.0
data = json.loads('{"a": 1}')   # parse JSON string into a Python dict
  • Use pip install <package> to install third-party packages (e.g., pip install requests).
  • Virtual environments keep each project's dependencies separate: python -m venv .venv creates one, then activate it with source .venv/bin/activate (Mac/Linux) or .venv\Scripts\activate (Windows).

21. Decorators

A decorator wraps a function to add extra behavior before or after it runs — without changing the function's code. The @decorator_name syntax is just shorthand for my_func = decorator(my_func).

import functools

def timer(func):
    @functools.wraps(func)   # preserves the original function's name & docstring
    def wrapper(*args, **kwargs):
        import time
        t0 = time.perf_counter()
        result = func(*args, **kwargs)   # call the original function
        print(f"{func.__name__} took {time.perf_counter()-t0:.4f}s")
        return result
    return wrapper

@timer   # this is equivalent to: slow = timer(slow)
def slow():
    import time; time.sleep(0.1)

slow()   # prints: slow took 0.1001s
  • Common built-in decorators you'll use often:
    • @staticmethod — a method that doesn't need self (doesn't access the object).
    • @classmethod — a method that receives the class itself as the first argument.
    • @property — makes a method behave like an attribute.

22. Core Standard Library Highlights

Python comes with a huge library of useful modules. Here are the most commonly used:

ModuleKey UsesExample
osFile system, env varsos.listdir(".")
sysInterpreter info, command-line argssys.argv[1]
pathlibObject-oriented file pathsPath("data") / "file.txt"
jsonJSON encode/decodejson.dumps({"a": 1})
reRegular expressions (pattern matching)re.findall(r"\d+", text)
datetimeDates and timesdatetime.now()
collectionsUseful data structuresCounter("abracadabra")
itertoolsEfficient looping toolsitertools.chain(a, b)
functoolsFunction utilities@functools.lru_cache (caching)
threadingRun code in parallel threadsthreading.Thread(target=f)
asyncioAsync I/O (non-blocking code)asyncio.run(main())
subprocessRun shell commands from Pythonsubprocess.run(["ls"])
dataclassesAuto-generate class boilerplate@dataclass

23. Quick Reference Cheat Sheet

# Variables with type hints (optional but helpful)
x: int = 42
name: str = "Alice"
items: list[int] = [1, 2, 3]

# Comprehensions
squares = [n**2 for n in range(1, 6)]
evens   = {n for n in range(10) if n % 2 == 0}

# Function with type hints
def greet(name: str, times: int = 1) -> str:
    return (f"Hello, {name}!\n") * times

# Class
class Point:
    def __init__(self, x: float, y: float):
        self.x, self.y = x, y

    def distance(self) -> float:
        return (self.x**2 + self.y**2) ** 0.5

# Error handling
try:
    val = int("abc")
except ValueError:
    val = 0

# File I/O
with open("data.txt", "w") as f:
    f.write("hello\n")

# Generator (infinite sequence)
def fib():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

# Async (for I/O-bound tasks like web requests)
import asyncio

async def fetch(url: str) -> str:
    await asyncio.sleep(1)   # simulate waiting for a network response
    return url

asyncio.run(fetch("https://example.com"))

Final Note

This guide covers all the fundamentals you need to start writing real Python programs. If something didn't click the first time — that's totally normal! The best way to learn is to write code, break things, and look things up.

When you're ready to go deeper, check out:

  • docs.python.org — the official Python documentation
  • python.org/about/gettingstarted — the official beginner's guide
  • Automate the Boring Stuff with Python by Al Sweigart — free online, very practical
  • Fluent Python by Luciano Ramalho — excellent once you know the basics

Happy coding!