Getting Started

Chapters

1

What Python Is (and Isn’t) Free

# What Python Is (and Isn’t) Python is one of those technologies that seems to show up everywhere: web apps, data science, automation, DevOps, embedded devices, education, game tooling, finance, cybersecurity, and even art. That popularity sometimes creates confusion—people hear “Python” and mentally bundle it with whatever they’ve recently seen: “Isn’t Python the AI language?” “Isn’t it a scripting language?” “Isn’t it slow?” “Isn’t it basically pseudocode?” This chapter clears the fog. We’ll define what Python actually *is* (a language, a set of implementations, a runtime, and an ecosystem), what it *isn’t* (not just a scripting language, not a magic bullet, not always slow, not only for beginners), and why those distinctions matter when you build real software. --- ## Python, precisely: a language specification and a family of implementations When people say “Python,” they often mean multiple things at once: 1. **The Python language**: a set of rules for syntax and semantics (“what code looks like” and “what it means”). 2. **An implementation**: a program that runs Python code (e.g., CPython, PyPy). 3. **The runtime environment**: the standard library, the object model, the memory model, the import system. 4. **The ecosystem**: packaging tools, third-party libraries, conventions, typing, linters, formatters, frameworks. ### The language vs. the interpreter A **language** is an idea. An **interpreter** (or compiler) is a piece of software that executes programs written in that language. * “Python” as a language includes features like indentation-based blocks, dynamic typing, first-class functions, generators, exceptions, context managers, pattern matching, and so on. * “CPython” is the reference implementation most people install when they download Python from python.org. * “PyPy” is another implementation with a JIT compiler that can be faster for certain workloads. * There are also implementations targeting other environments like Java (Jython historically) or .NET (IronPython), and specialized ones like MicroPython for microcontrollers. Why this matters: sometimes people complain about Python’s speed, memory usage, or concurrency and blame “Python,” when the actual limitation belongs to **a specific implementation** (most commonly CPython), or to the nature of the workload. --- ## Python is a general-purpose programming language Python wasn’t created as a niche tool. It’s **general-purpose**, meaning it’s designed to be useful across many domains rather than optimized for just one. You can build: * command-line tools and automation scripts * web backends and APIs * data pipelines and ML systems * desktop apps and internal tools * network services and distributed systems * test frameworks and build systems * game tooling and content pipelines General-purpose doesn’t mean “best at everything.” It means “excellent at many things, and capable at most.” --- ## Python is “high-level” (and that’s a feature, not an insult) A **high-level language** abstracts away low-level machine concerns so you can focus on modeling problems: data structures, domain logic, interactions, and correctness. Python gives you: * automatic memory management (garbage collection + reference counting in CPython) * powerful built-in data types (`list`, `dict`, `set`, `tuple`) * rich introspection and a dynamic object model * exceptions instead of error codes * batteries-included standard library (files, networking, parsing, concurrency primitives) High-level means you can express solutions clearly and quickly. It also means you sometimes trade off raw performance or low-level control. If you’ve ever shipped something meaningful in production, you know: **clarity and correctness compound**. Code is read far more than it is written. --- ## Python is dynamically typed (but not “untyped”) One of the most common misunderstandings: > “Python doesn’t have types.” It absolutely does. Everything in Python has a type. What Python *doesn’t require* is that you declare those types in advance. ```python x = 10 # x is an int x = "ten" # now x is a str ``` This is **dynamic typing**: variable names can be rebound to objects of different types over time. Dynamic typing can speed up exploration and reduce boilerplate, but it also means some mistakes are caught at runtime instead of compile time. ### Modern Python: optional static typing Today’s Python supports **type hints**, which let you write clearer APIs and enable tooling (autocomplete, refactoring, type checking): ```python def total(prices: list[float]) -> float: return sum(prices) ``` Type hints are not enforced by the interpreter (by default). They’re checked by tools like mypy, pyright, or your IDE. This “best of both worlds” approach is one reason Python continues to scale into larger codebases. So Python isn’t “untyped.” It’s **dynamically typed with optional static typing**. --- ## Python is interpreted… kind of People often say: > “Python is an interpreted language.” That’s broadly true, but incomplete. Most Python you run is executed by an interpreter, but Python code is typically compiled first into **bytecode**, then executed by a **virtual machine**. In CPython: 1. Your `.py` file is parsed into an AST. 2. It’s compiled to bytecode (`.pyc`). 3. The CPython VM executes that bytecode. This matters because it explains why: * Python can do some compile-time checks (like syntax errors) before running. * Python can cache compiled bytecode for faster startup on subsequent runs. * “Interpreted vs compiled” is not a clean binary; it’s a spectrum. --- ## Python is a language with multiple paradigms Python supports several styles of programming, and good Python code often blends them: ### Procedural programming Straightforward step-by-step logic, functions, and control flow. ### Object-oriented programming (OOP) Classes, encapsulation, protocols, polymorphism. Python’s OOP is pragmatic: it supports classes well, but doesn’t force everything into objects. ### Functional programming First-class functions, higher-order functions, comprehensions, `map`, `filter`, lambdas (limited but useful), immutability patterns, and powerful iterators. ### Async programming `async`/`await` enables high-performance I/O concurrency for servers, bots, crawlers, and network tools. Python is not “purely” any one of these—and that’s why it fits so many problems. --- ## Python is designed for readability (and opinionated about it) Python’s most famous feature is its use of indentation to define blocks: ```python if condition: do_something() else: do_something_else() ``` Some languages let you write messy code. Python lets you write messy logic, sure—but the syntax nudges you toward readable structure. This design decision is controversial to some newcomers, but it pays dividends in: * consistent code style across projects * reduced visual noise (no braces) * fewer “where does this block end?” bugs * easier onboarding for teams Python also encourages clear code through: * expressive naming conventions * straightforward standard library APIs * a culture of style guides (PEP 8) and tooling (formatters like Black) Readability isn’t cosmetic. It’s a productivity multiplier. --- ## Python is an ecosystem, not just a syntax If Python were only syntax, it wouldn’t dominate so many fields. What makes Python powerful is the combination of: * a large standard library * a thriving open-source community * package distribution (PyPI) * mature frameworks and tooling In practice, “Python” often means “Python + NumPy + pandas,” or “Python + Django,” or “Python + FastAPI,” or “Python + requests,” or “Python + asyncio.” The language makes it easy to write glue code; the ecosystem provides the building blocks. --- # What Python isn’t Now for the myths, half-truths, and mislabels that cause bad decisions. ## Python isn’t “just a scripting language” Python *can* be used as a scripting language—short programs, automation tasks, one-off utilities. But it’s not limited to that. Large companies run massive Python systems in production. Python codebases can be: * hundreds of thousands to millions of lines * heavily typed with hints * modularized with packages, layered architectures, and strict CI rules * performance-tuned with native extensions or optimized runtimes The “scripting language” label usually means “good for quick tasks.” Python is good for quick tasks—*and* for serious systems. ## Python isn’t automatically slow Python can be slower than C, Rust, or Java for CPU-bound work, yes. But “Python is slow” misses the point: 1. Many real workloads are **I/O-bound** (waiting on network, disk, DB). Python performs very well here. 2. Performance bottlenecks often live in a small fraction of code; optimizing that part can yield most benefits. 3. In data science and ML, heavy lifting is often done in optimized C/C++/CUDA libraries behind Python APIs. 4. Alternative runtimes (like PyPy) can speed up certain workloads significantly. 5. You can move hotspots into native code (Cython, Rust extensions, C extensions) when needed. The real claim should be: > Python is not the fastest language for tight CPU loops, but it is often fast enough—and can be made faster where it matters. ## Python isn’t the best choice for everything Python is a fantastic default, but there are cases where it’s not ideal: * **hard real-time systems** where latency must be deterministic * **ultra-low-latency trading** where microseconds matter everywhere * **mobile apps** where native platform languages dominate * **highly constrained embedded systems** (though MicroPython exists) * **very large-scale parallel CPU-bound compute** (possible, but requires careful design) Python is a tool. A great one. But mature engineers choose tools based on constraints, not vibes. ## Python isn’t a “toy language” for beginners Python is beginner-friendly, but that’s not the same as being simplistic. Python’s object model is deep. Its metaprogramming capabilities are strong. The standard library is large. The ecosystem supports everything from formal verification efforts to distributed computing. “Beginner-friendly” is a compliment: it means the language is approachable, not that it lacks power. ## Python isn’t “weakly typed” This one is subtle. * **Weakly typed** typically means a language will implicitly convert between unrelated types in surprising ways (like turning `"5"` into `5` in arithmetic). * Python generally doesn’t do that. It will often raise an error instead. ```python "5" + 1 # TypeError ``` Python is **strongly typed**, but **dynamically typed**. That’s a useful distinction when you reason about code correctness. ## Python isn’t always the same across environments Write-once-run-anywhere is a goal, but Python behavior can differ based on: * implementation (CPython vs PyPy) * OS differences (file paths, permissions, subprocess quirks) * architecture (32-bit vs 64-bit) * installed dependencies and version mismatches This is why professional Python development emphasizes: * pinned dependencies * reproducible environments (venv/uv/conda/poetry) * CI tests across platforms * containerization where appropriate “Works on my machine” is not a Python problem—it’s a software engineering problem. But Python makes environment management important because the ecosystem is so broad. --- ## The mental model: Python as a productivity language A useful way to think about Python is: * Python optimizes for **developer time** over machine time. * It favors **clarity** over cleverness. * It encourages **composition** and **reuse** through modules and packages. * It provides a “glue layer” that integrates components written in faster languages. This is why Python dominates in domains where iteration speed matters: data exploration, prototyping, automation, internal tooling, startups, and increasingly even enterprise systems. --- # A practical comparison: where Python shines, where it struggles ## Shines * rapid prototyping and iteration * automation and scripting * web APIs and backend services * data wrangling and analysis * machine learning workflows * test automation and tooling * education and readability-driven teams ## Struggles (without extra work) * heavy CPU-bound number crunching written in pure Python loops * extremely low-latency requirements * strict real-time constraints * distributing CPU workloads across cores efficiently in CPython without using multiprocessing/native approaches “Struggles” doesn’t mean “can’t.” It means “you’ll need the right patterns.” --- # Why this chapter matters It’s easy to learn Python syntax and write programs that work. It’s harder—and more valuable—to understand what Python *is*, because that shapes: * performance expectations * architecture choices * tooling decisions * how you test and deploy * how you scale teams and codebases If you think Python is “just a scripting language,” you’ll avoid building serious systems in it—and miss out on a very effective platform. If you think Python is “slow,” you might rewrite code in a lower-level language prematurely, paying a huge complexity tax. If you think Python “has no types,” you might ignore modern typing tools that prevent bugs and improve maintainability. The truth is more nuanced, and that nuance is power. --- ## Quick recap Python is: * a general-purpose, high-level programming language * strongly typed but dynamically typed (with optional static typing) * executed by implementations like CPython and PyPy * multi-paradigm (procedural, OOP, functional, async) * readability-first, backed by a huge ecosystem Python isn’t: * only a scripting language * inherently slow in all contexts * a toy language * weakly typed * the best fit for every single use case --- ## Mini exercise: test your understanding Try answering these in one sentence each: 1. What’s the difference between Python (the language) and CPython? 2. What does “strongly typed but dynamically typed” mean? 3. Why might Python be “fast enough” for many production systems? 4. Name one case where Python might not be the best tool without extra work. Write your answers down—even if you’re not sure. The goal is to internalize the concepts, not to be perfect.

2

Setup like a pro

# Setup Like a Pro (Python 3.14 Edition) A professional Python setup isn’t about having the fanciest tools. It’s about **removing surprise**. Surprise is what happens when: * one project silently upgrades your dependencies and breaks another * “Python works” on your machine but not your teammate’s * you install a package globally and six weeks later can’t remember *where* it came from * Windows can’t find `python`, macOS runs a different `python3` than you expected, Linux permissions get weird The goal of this chapter is to give you a setup that is: * **stable** (doesn’t break your OS) * **repeatable** (same commands across projects) * **portable** (macOS / Windows / Linux) * **team-friendly** (easy to explain, easy to debug) We’ll target **Python 3.14** specifically (current downloads are in the 3.14.x line). ([Python.org][1]) --- ## The two rules that prevent 90% of Python setup problems ### Rule 1: Don’t touch “system Python” for projects Your operating system may use Python internally—especially on Linux. Treat that installation as *owned by the OS*. You can absolutely use it to *run Python*, but you shouldn’t install project dependencies into it. ### Rule 2: One virtual environment per project Every project gets its own isolated environment: * its own `pip` * its own dependency versions * its own “it works on my machine” boundary This is the difference between “Python as a hobby” and “Python as a professional toolchain.” --- ## What we’re going to build A “pro” baseline that works everywhere: 1. Install **Python 3.14** (prefer official installers on Windows/macOS; use distro packages or a version manager on Linux) 2. Use **`.venv`** per project (built-in `venv`) 3. Use **`pip` inside the venv** for project dependencies 4. Use **`pipx` for global CLI tools** (so you don’t pollute projects with tool installs) ([pipx.pypa.io][2]) Optionally (recommended once you juggle multiple projects): * use a version manager like **pyenv** or **uv** (both are common in real teams) ([GitHub][3]) --- ## First: confirm what “Python 3.14” means on your machine Python 3.14 is a major release line, and you’ll typically install a patch version like **3.14.3** (patch releases contain bug/security fixes). The python.org downloads page shows the current latest in the 3.14 series. ([Python.org][1]) After installation, your first verification commands are always: ```bash python3 --version python3 -m pip --version ``` On Windows, also: ```powershell py --version py -0p ``` (We’ll explain `py` in the Windows section.) --- # macOS (Python 3.14) macOS is usually smooth—until you accidentally mix multiple Pythons. The fix is simple: pick a primary installation method, then verify your PATH. ## Option A: Official python.org installer (simple, predictable) 1. Download Python 3.14.x for macOS from python.org. ([Python.org][1]) 2. Install it (standard macOS installer flow). 3. Verify: ```bash python3 --version which python3 python3 -m pip --version ``` You want `python3 --version` to show `3.14.x`. ### Why this is “pro” * You’re aligned with the reference distribution * Tooling and docs match what most people assume ## Option B: Homebrew (great if you already live in brew) Homebrew can install Python 3.14 specifically via `[email protected]`. ([Homebrew Formulae][4]) ```bash brew install [email protected] ``` Verify: ```bash python3 --version which python3 ``` ### One important note about Homebrew Brew can also install “python” (unversioned), which may advance to newer versions. If your book/project targets *3.14*, installing `[email protected]` keeps you anchored. --- ## macOS: create your first professional project environment This is the workflow you’ll reuse constantly. ```bash mkdir hello314 cd hello314 python3 -m venv .venv source .venv/bin/activate python -m pip install --upgrade pip python -m pip --version python --version ``` At this point, `python --version` should report `3.14.x`, and your `pip` is the one inside `.venv`. Install a dependency: ```bash python -m pip install requests python -c "import requests; print(requests.__version__)" ``` Freeze for reproducibility: ```bash python -m pip freeze > requirements.txt ``` When you’re done: ```bash deactivate ``` ### Pro habit: always run pip through python Inside a venv, prefer: ```bash python -m pip install ... ``` It guarantees you’re installing into the interpreter you’re actually using. --- ## macOS: install global CLI tools safely with pipx You’ll want tools like linters, formatters, cookiecutters, REPL helpers, build tools. Installing them into every project is wasteful, and installing them globally with `pip` gets messy. `pipx` installs each CLI tool into its own isolated environment and exposes the executable on your PATH. ([GitHub][5]) On macOS: ```bash brew install pipx pipx ensurepath ``` Or using Python’s pip (also valid): ```bash python3 -m pip install --user pipx python3 -m pipx ensurepath ``` Now you can do: ```bash pipx install ruff pipx install pytest ``` (We’ll talk about these tools later; the point is: **pipx keeps tools clean**.) --- # Windows (Python 3.14) Windows setup is extremely workable, but you have to understand one key concept: > On Windows, the **Python Launcher** (`py`) is your best friend. It exists specifically to locate and run installed Python versions. ([Python Enhancement Proposals (PEPs)][6]) ## Install Python 3.14 from python.org Go to python.org downloads and choose the Windows installer for Python 3.14.x. ([Python.org][1]) During installation, you’ll typically want: * ✅ Add Python to PATH (helpful) * ✅ Install the launcher (`py`) (very helpful) After installing, open **PowerShell** and verify: ```powershell py --version python --version py -0p ``` ### What `py -0p` tells you It lists installed Python versions and their paths, which is incredibly useful when debugging “wrong Python” problems. The official Windows docs also note that once you install the Python install manager/launcher, `python` may point to your “current latest” installation and can change as versions are added/removed—another reason `py` is so useful. ([Python documentation][7]) --- ## Create a virtual environment on Windows (PowerShell) ```powershell mkdir hello314 cd hello314 py -3.14 -m venv .venv .\.venv\Scripts\Activate.ps1 python --version python -m pip install --upgrade pip ``` If activation is blocked by PowerShell policy, set it for your user: ```powershell Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser ``` Then activate again. Install a dependency: ```powershell python -m pip install requests python -c "import requests; print(requests.__version__)" python -m pip freeze > requirements.txt ``` Deactivate: ```powershell deactivate ``` ### Pro tip: specify the version you mean When you want Python 3.14 specifically: ```powershell py -3.14 --version py -3.14 -m venv .venv ``` This keeps you honest, especially if you have multiple versions installed. --- ## Windows: pipx for global tools Install pipx: ```powershell py -m pip install --user pipx py -m pipx ensurepath ``` `ensurepath` makes sure the pipx apps directory is on your PATH (you may need to restart the terminal). ([packaging.python.org][8]) Then: ```powershell pipx install ruff pipx install pytest ``` --- # Linux (Python 3.14) Linux is the most variable because distributions ship different versions at different times. Your choices are: * **Use the distro Python** if it’s already 3.14 (or if 3.14 isn’t required for your work yet) * **Use a version manager** to install Python 3.14 without touching system Python (recommended for consistency across machines) Python 3.14 is a stable release line (3.14.0 released Oct 7, 2025), and patch releases follow. ([Python.org][9]) ## Baseline: install venv tools from your distro Example (Debian/Ubuntu-ish): ```bash sudo apt update sudo apt install -y python3 python3-venv python3-pip ``` Then: ```bash python3 --version python3 -m venv --help ``` If this reports Python 3.14.x, you’re good. ### Create a project venv (Linux) ```bash mkdir hello314 cd hello314 python3 -m venv .venv source .venv/bin/activate python --version python -m pip install --upgrade pip ``` --- ## Pro path on Linux: install Python 3.14 with a version manager When your distro doesn’t ship 3.14 yet (or you want identical versions across machines), a version manager keeps your system clean. ### Option A: pyenv (classic, widely used) pyenv installs Python versions into your user space and lets you choose a version globally or per directory. ([GitHub][3]) High-level steps: 1. Install build dependencies (varies by distro; Ubuntu/Debian commonly needs build libs) 2. Install pyenv 3. Install Python 3.14.x: ```bash pyenv install 3.14.3 pyenv local 3.14.3 python --version ``` Then you can create a venv using that Python. ### Option B: uv (modern, very fast) `uv` can install Python and manage environments, designed to be fast and cross-platform. ([Astral Docs][10]) One nice thing about uv is that it can handle “get me Python 3.14” without you wiring up compilers manually on many systems, and it has explicit commands for installing Python versions. ([Astral Docs][11]) (You can keep using `venv` + `pip` even if you use uv to install Python. Don’t feel forced into an all-or-nothing tool switch.) --- # The “real book” workflow: the setup you’ll reuse for every project Here’s the setup I expect in a professional repository: 1. Python version is known (3.14.x) 2. Each project has `.venv` 3. Dependencies are recorded 4. Tools are consistent across contributors ## Step 1 — Create project and venv **macOS/Linux** ```bash mkdir my_project cd my_project python3 -m venv .venv source .venv/bin/activate ``` **Windows PowerShell** ```powershell mkdir my_project cd my_project py -3.14 -m venv .venv .\.venv\Scripts\Activate.ps1 ``` ## Step 2 — Upgrade pip inside the venv ```bash python -m pip install --upgrade pip ``` ## Step 3 — Install project dependencies ```bash python -m pip install requests ``` ## Step 4 — Record dependencies (two common styles) ### Style A: `requirements.txt` (simple, common) ```bash python -m pip freeze > requirements.txt ``` ### Style B: “top-level deps” + lock (more controlled) Many teams keep a short list of direct deps and generate a fully pinned lock file. If you’re early in your journey, start with Style A and upgrade later. --- # Setup your editor like a pro (without overcomplicating it) No matter which editor you use, professionals want three things: * the editor uses the **project interpreter** (`.venv`) * formatting is consistent * linting catches issues early ## The one editor rule that matters Make sure your editor is pointed at: * `.venv/bin/python` (macOS/Linux) * `.venv\Scripts\python.exe` (Windows) If your editor uses a different Python, you’ll see weird behavior like: * imports failing in the editor but working in terminal * linting complaining about missing packages you installed * different Python versions between runs --- # Global tools: what belongs in pipx vs inside the project A clean mental model: ### Install via pipx (global CLI tools) * formatters / linters (`ruff`) * test runners (`pytest`) *optional to keep global* * scaffolding tools (`cookiecutter`) * CLI utilities (`httpie`, `pip-tools`, etc.) pipx is designed exactly for this “install CLI tools, keep them isolated” use case. ([GitHub][5]) ### Install inside the project venv * runtime dependencies (`requests`, `fastapi`, `numpy`) * project-specific tooling that must match the project (certain plugins, pinned toolchains) Many teams install `pytest` and `ruff` inside the project to guarantee versions. Others keep them global via pipx. Either can be “pro”—the key is consistency. --- # Troubleshooting: the professional “doctor commands” When Python behaves strangely, don’t guess. Inspect. ## macOS/Linux doctor commands Outside venv: ```bash which python3 python3 --version python3 -m pip --version ``` Inside venv: ```bash which python python --version python -m pip --version python -c "import sys; print(sys.executable)" ``` ## Windows doctor commands (PowerShell) ```powershell py --version py -0p where python python --version python -c "import sys; print(sys.executable)" ``` If `sys.executable` isn’t pointing into `.venv`, you’re not running the environment you think you are. --- # Common “pro” mistakes (and fixes) ## Mistake: installing packages globally Symptom: your project works… until it doesn’t. Or a new project breaks an old one. Fix: always create `.venv` and install dependencies there. ## Mistake: mixing multiple Python installations on macOS Symptom: `python3` changes depending on terminal, or VS Code uses a different interpreter than your shell. Fix: pick one install path (python.org or brew), verify `which python3`, and set your editor interpreter explicitly. ## Mistake: Windows “python opens Microsoft Store” Symptom: typing `python` triggers Store prompts. Fix: use `py` (launcher) and ensure the official Python install + launcher is present. The launcher exists exactly to solve this problem. ([Python Enhancement Proposals (PEPs)][6]) --- # Chapter recap A professional Python 3.14 setup looks like this: * Install **Python 3.14.x** from a trusted source (python.org is the baseline; Homebrew on macOS is fine; Linux may need pyenv/uv). * Use **`.venv` per project** * Use `python -m pip ...` to avoid installing into the wrong interpreter * Use **pipx** for global CLI tools to keep things clean * On Windows, prefer **`py`** to reliably select Python versions

3

Your first workflow