Skip to content

Python

Python is a general-purpose, dynamically typed language designed so that reading code is as easy as writing it. It runs everywhere, ships with a large standard library, and has become the default language for scripting, automation, data work, and backend services.

This section teaches the language itself — the object model, the syntax, the standard library, and the idioms that separate code that merely works from code a Python programmer would recognise as correct.

Python is a specification (the language reference) with several implementations. The one you almost certainly have is CPython, the reference implementation written in C, distributed from python.org and by every OS package manager.

Other implementations exist and matter occasionally:

Implementation What it is Why you’d care
CPython The reference implementation The default; C extension modules target it
PyPy A tracing JIT compiler Much faster for long-running pure-Python loops
MicroPython Trimmed Python for microcontrollers Embedded devices with kilobytes of RAM
GraalPy Python on the GraalVM Interop with the JVM ecosystem

Unless a page says otherwise, “Python” here means CPython 3.12 or newer.

Python is often called “interpreted”, which is half the story. CPython does compile — just not to machine code:

  1. Your .py source is parsed into an abstract syntax tree.
  2. The tree is compiled to bytecode, a compact instruction set for a stack machine.
  3. A C loop (the evaluation loop) executes that bytecode.

The bytecode for imported modules is cached in a __pycache__ directory so the compile step is skipped next time. You never manage those files; deleting them is always safe.

import dis
def add(a, b):
return a + b
dis.dis(add)
2 RESUME 0
3 LOAD_FAST_LOAD_FAST 1 (a, b)
BINARY_OP 0 (+)
RETURN_VALUE

Python 2 reached end of life on 1 January 2020. Everything here is Python 3. Within the 3.x line, each minor release (3.11, 3.12, 3.13, 3.14…) lands every October and gets roughly five years of support: two years of bugfixes, then three of security fixes only.

Minor releases are largely backward compatible but do remove long-deprecated things, so “Python 3” is not one target. Milestones worth remembering:

  • 3.6 — f-strings, ordered dicts as an implementation detail
  • 3.7 — dataclasses, dicts ordered by specification
  • 3.8 — the walrus operator :=, positional-only parameters
  • 3.9 — built-in generics (list[int]), dict merge operator |
  • 3.10 — structural pattern matching (match), X | Y union types
  • 3.11 — large interpreter speedups, exception groups, tomllib
  • 3.12 — new type-parameter syntax, f-string grammar relaxed
  • 3.13 — improved REPL, experimental free-threaded build

Check what you are running before anything else:

Terminal window
python3 --version
# => Python 3.12.7

There are four ways you will invoke Python, and they behave differently.

A script file. The most common. The file’s directory is prepended to the module search path.

Terminal window
python3 script.py
python3 script.py --flag value # arguments land in sys.argv

The REPL (read–eval–print loop). Type python3 with no arguments. Expressions echo their value automatically, _ holds the last result, and exit() or Ctrl-D leaves. Python 3.13 shipped a substantially better REPL with multiline editing, colour, and paste support.

Terminal window
python3
>>> 2 ** 100
1267650600228229401496703205376
>>> _ % 7
2

python3 -m module. Runs an installed module or package as a script, resolving it through the import system rather than the filesystem. This is how you invoke tooling correctly, because it guarantees you get the tool belonging to this interpreter.

Terminal window
python3 -m venv .venv # create a virtual environment
python3 -m pip install requests
python3 -m http.server 8000 # serve the current directory
python3 -m json.tool data.json # pretty-print JSON
python3 -m timeit '"-".join(str(n) for n in range(100))'

python3 -c "code". A one-off snippet, useful in shell pipelines.

Terminal window
python3 -c "import sys; print(sys.version_info)"

An executable script on Unix gets a shebang plus the execute bit:

hello.py
#!/usr/bin/env python3
print("hello")
Terminal window
chmod +x hello.py
./hello.py

Python has no braces and no end keyword. A block is defined by indentation, and the compiler enforces it. This is not a style preference — wrong indentation is a SyntaxError or, worse, a silent logic change.

def classify(n):
if n < 0:
return "negative"
elif n == 0:
return "zero"
return "positive"

The rules: a colon opens a block, the block is indented more than its header, and every line in a block uses the same indentation. PEP 8 says four spaces per level. Never mix tabs and spaces — Python 3 rejects files that do so ambiguously.

def broken():
x = 1
y = 2 # IndentationError: unexpected indent

pass is the no-op statement for a block you have not written yet:

def todo():
pass

These two properties are independent and often confused.

Dynamically typed — types belong to values, not to variables. A name can be rebound to a different type at any time, and type errors surface at runtime.

x = 42
x = "now a string" # perfectly legal

Strongly typed — Python will not silently coerce unrelated types to make an operation work.

"3" + 4
# TypeError: can only concatenate str (not "int") to str

Compare with JavaScript, where "3" + 4 quietly yields "34". Python makes you say what you mean:

int("3") + 4 # => 7
"3" + str(4) # => '34'

Everything in Python is an object: integers, functions, classes, modules. Every object has an identity, a type, and a value. That uniformity is why functions can be passed around like data and why classes can be built at runtime.

Type hints (covered in typing and async) let you annotate the types you intend. They are checked by external tools, never by the interpreter.

A PEP is a Python Enhancement Proposal — the design documents of the language. Two are cultural bedrock.

PEP 8 is the style guide. The essentials:

Thing Convention Example
Indentation 4 spaces
Line length 79 chars (many projects use 88 or 100)
Functions, variables lower_snake_case parse_config
Classes CapWords HttpClient
Constants UPPER_SNAKE_CASE MAX_RETRIES
“Internal” names leading underscore _cache
Modules short, lowercase utils.py

Nobody formats by hand any more. Black and Ruff reformat automatically, and Ruff also lints:

Terminal window
python3 -m pip install ruff
ruff format .
ruff check .

The Zen of Python (PEP 20) is the design philosophy, printed by an easter-egg module:

Terminal window
python3 -c "import this"

The lines that actually change how you write code: explicit is better than implicit, flat is better than nested, errors should never pass silently, and there should be one — and preferably only one — obvious way to do it.

Read in order if Python is new to you; jump around if it is not.

  1. Types and variables — numbers, strings, None, and what a variable really is.
  2. Data structures — lists, tuples, dicts, sets, comprehensions, copying.
  3. Control flow and functions — branching, loops, match, the full function-argument model, scope, closures, decorators.
  4. Object-oriented Python — classes, properties, inheritance, dunder methods, dataclasses.
  5. Modules and environments — imports, packages, pip, virtual environments, project files.
  6. Errors and context managers — exceptions and with.
  7. Iterators and generators — lazy evaluation and itertools.
  8. Standard library and idioms — the modules you reach for daily, and how Python code is meant to read.
  9. Typing and async — type hints, mypy, async/await, asyncio.
  • CPython compiles to bytecode and runs it in an interpreter loop; __pycache__ is just a compile cache.
  • Use python3 (or py on Windows); check --version before debugging anything.
  • python3 -m tool runs the tool belonging to the interpreter you invoked — prefer it.
  • Indentation is grammar, four spaces, never mixed with tabs.
  • Dynamic typing means types travel with values; strong typing means no silent coercion.
  • PEP 8 for style (enforced by Ruff or Black), PEP 20 for judgement.