GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • 01Introduction
  • 02Tour
  • 03Containers
  • 04Control Flow
  • 05Functions
  • 06Modules and Packages
  • 07Classes
  • 08Static Types
  • 09Class Attributes
  • 10Cleanup
  • 11Testing
  • 12Data Classes as Types
  • 13Pattern Matching
  • 14Decorators

This view

  • Original page
  • New address

GeezerVison is showing thinkinginpython.com — Introduction · Thinking in Python

Index
Contents
Chapter 1

Introduction

This book is about developing the judgment to choose the smallest thing that works. You build that judgment through insights, idioms, and patterns. The book also questions design patterns. Most arose to work around the limits of static, inheritance-heavy languages, so in Python many of them diminish or dissolve. If an idiom or pattern is still useful, it stays.

Every language has habits worth learning and habits worth dropping. Programmers who come to Python from C++ or Java arrive with patterns, ceremonies, and defensive structures that those languages make necessary. Python needs far fewer of these. Instead of a class that exists only to hold one method, Python has functions. A Singleton is a module. A Visitor is a function that dispatches on type. Before using an idiom or pattern, this book asks whether the language already solves the problem, and adds the complexity only when the answer is no.

AI Trigger Warning

I started this book in 2008, with the idea of taking the design patterns work I had done in Java and translating it to Python. In 2011 I abandoned the project with many of the design patterns still in Java. Eventually I even wrote a message confirming I would not complete it.

In June of 2026, after people mentioned the online book to me at recent PyCons, I decided to see what the Claude AI could do with it. The experience was amazing, and I began adding material from my blog posts and presentations. Claude allowed me to create tooling for the book that I had imagined but never fully realized.

This book would not exist without the help of Claude. That said, it is still my work, derived from existing work, designed by me, checked and rewritten by me. It has my voice, and I’ve gone over every sentence multiple times, editing, rewriting, and adding.

I know some people don’t like AI. Without it, this book wouldn’t exist. The book is free, so if AI bothers you more than the book might benefit you, please ignore the book.

Using Claude made me realize how many compromises I’ve made on books in the past. I would get a good idea about something (for example, automatically interleaving commented output in the listings). I either couldn’t implement it, or it seemed too hard, so I didn’t do it. But with AI I can explore and often implement every whim, from things as seemingly straightforward as inserting a new chapter to ones as daunting as that commented-output system. The result is much better than anything I managed before. I keep going until I’ve tweaked everything that occurs to me.

For things I have generated rather than written from scratch, I’ve become the director of the movie instead of an actor in it. This book is what I’ve always wanted to create, but have always lacked the capacity to flesh out in all its myriad detail. With Claude and my directing and rewriting, I can build my ideal book.

Using Claude greatly simplified and sped the writing process. Claude did not make that process trivial. Claude first translated and integrated my own work into the book, and generated the first versions of new material. Then I went through line by line and concept by concept, inevitably rewriting and asking for clarification, often creating more examples to answer further questions. One pass would often change the book enough that it needed another pass. The process was much faster with Claude, but it still took time and effort. Claude provided excellent support, but did not replace my own work.

I can’t predict the future of books. The internet and eBooks have been changing the print book industry for decades. But with AI, how many people will keep reading computer programming books? I hardly do. If I need something, I ask AI. My only hope is that this book will be engaging to read, and that you will experience some of the same satisfaction that I’ve had while writing it.

I think most programmers will regularly use AI, if they don’t already. Perhaps I am teaching the equivalent of assembly language after everyone has started using the equivalent of compilers. However, at the time of this writing, Python seems to be the most popular language for AI-generated code. Some small percentage of people might still wish to analyze what the AIs generate. This book might have some value yet.

The knowledge in this book has helped me guide AIs toward better solutions.

Who This Book Is For

I am writing for the programmer who knows how to program, either in another language or in Python. The goal is to move from writing Python that works to writing Python that is clear, idiomatic, and a pleasure to maintain.

This is an intermediate-to-advanced book, which removes two constraints that introductory books follow:

  1. An introductory book must describe everything in lockstep, never using an idea before formally introducing it. This one uses an idea wherever it helps, sometimes before the chapter that covers it.
  2. An introductory book chooses topics by where they fall in a beginner’s path. This one chooses them by whether they are interesting and useful.

You should be comfortable with:

  • Functions, classes, objects, and inheritance
  • Containers: lists, dictionaries, tuples, and sets

If a language feature is new to you, look it up as you go.

You do not need to know design patterns, metaclasses, or type checking. This book covers them. The book is about the language, not IDE setup or deployment.

How the Book Fits Together

You can read straight through, or jump to a chapter that interests you, since most chapters are self-contained. The book has five parts.

Part I, Foundations, is a fast tour of the language: its syntax, containers, control flow, functions, modules, classes, static typing, class attributes, and object cleanup. This part is for programmers coming to Python from another language. If you know Python, you can skim for topics new to you, or skip it altogether.

If you skip Part I, come back for Static Types. Every chapter after Static Types annotates its examples, so Static Types is the chapter in Part I that every later chapter assumes you have read. Class Attributes and Cleanup are the two others that a reader who knows Python should still read, since later chapters build on ClassVar, bare annotations, and weak references.

Part II, Techniques, covers the idioms and tools that give Python its character: testing, data classes as types, pattern matching, decorators, context managers, comprehensions, and metaprogramming. It closes with performance and concurrency, where the question changes from what the code says to how fast it runs. Many of these chapters came from presentations I’ve given, mostly at PyCon.

Part III, Patterns, opens by stepping back to question object orientation, because several of the patterns that follow exist to manage problems that objects create. The next chapter introduces the design-patterns movement, and the question I keep asking through the rest of the part: what problem are you solving, and does the language already do the pattern’s job? Learning to ask those questions is one of the most useful things this book can give you. The part then works through the classic design patterns, reframing each for Python and weighing it against the language. The part ends by refactoring one problem through several designs, building simulations out of the pieces, and cataloging the classic patterns together with the ones the literature added later.

Part IV, Functional, covers pure functions, the functools and itertools toolkits, errors returned as values instead of raised exceptions, and a spectrum of confidence that runs from local reasoning up to machine-checked proof.

Part V, Effects, closes the book by covering everything a program does that a pure function cannot. One chapter surveys languages that track Effects in a function’s type, and asks what Python could adopt. Another develops the full generator protocol. The last two put Effect tracking and generators to work with Stateless, a library that builds Effect tracking on generators and brings it to Python today. Read these four chapters in order. The last two build on the chapters before them.

Two appendices follow. Effect Tracking asks how much Effect tracking Python’s Annotated type can express without a library, and An Effect Checker builds a checker for those annotations. The checker works without type inference, resolving a call by its name and by its receiver’s type, where the source writes that type out or makes it evident.

The Examples

The book targets Python 3.15 and later, uses type annotations, and tests with pytest. Early chapters mostly omit type annotations, until Static Types introduces the syntax. Every chapter afterward uses them consistently.

Every code block that begins with a filename comment, like # tracer.py, is a file rather than a fragment. A long file can span several blocks. Each later block begins # tracer.py (continued), and the parts join in order into one file. Most of these files run on their own. Some are modules that another listing imports. The test_*.py files hold tests that pytest runs. All of these files, tests included, live in the Examples/ directory of the source repository, one folder per chapter, so the listing starting with # tracer.py in Decorators is the file Examples/14_Techniques--Decorators/tracer.py.

A helper that more than one chapter uses carries a utils/ path in its filename comment, like # utils/result.py, and lives in Examples/utils/ rather than in a chapter folder. A listing imports such a helper by its bare module name, from result import Err, Ok, because the example tooling puts Examples/utils/ on the import path.

A file run straight from the repository root lacks Examples/utils/ on its import path. The repository comes with a task runner, tip, that has a command for running one example. tip run-one <name> (tools/run_one_example.py) runs the file from its chapter folder with Examples/utils/ on the import path, and prints the equivalent by-hand commands first.

The short form, tip <name>, leaves out run-one. tip membership runs membership.py. Words after the name go to the program, so tip membership --numbers passes the --numbers flag to membership.py. For a test_*.py file the short form runs pytest on it. When the book says to run a listing, use the short form.

A filename comment that starts with rust/, like the # rust/fastcount/demo.py listing in Performance, names a file in the repository’s rust/ directory, outside the Python build. Chapter 44’s Koka, Flix, Scala, and TypeScript programs open with a // path comment, such as // koka/greet.kk, and live in the repository’s effects/ directory.

uv and other tools make setup short. The repository’s README has the instructions, including how to install tip. tools/README.md explains how to build the book and run the examples yourself.

The book’s build system extracts the examples, then type-checks (with Astral’s ty), lints (with ruff), runs, and tests them. The code you read is the code that runs, and the output you see is the output it produces.

A few early listings make a type error on purpose and carry a # type: ignore comment on that line. The comment tells the type checker to suppress the error it would report there. Static Types covers type checking in full, along with the # ty: comments that appear from that chapter on.

Output appears inside the listings as comments beginning with #:, one line of output per marker. A print("affirmative") above a line reading #: affirmative means the program prints affirmative at that point. A group of markers shows everything the code above it printed since the previous group, in order. Output from inside a loop or an import therefore appears in the group of markers after the loop or the import, not next to the line that produced it. The build verifies these markers against a real run, so they always match what the code prints.

When the text walks through a listing line by line, a trailing comment such as # [1] tags a line, and the text names the line by its bracketed number. Tags count up from one within each listing.

If you find a mistake, please send a correction. See CONTRIBUTING.md in the source repository.

The Exercises

Most chapters end with a short “Exercises” section. The exercises come from workshops, where pairs work through them at a keyboard. They are short enough to do on your own, and they are worth doing that way. They usually ask you to copy a small, working example from their chapter, change it, and observe the result: add a class, break an invariant on purpose, extend a table, rewrite one function two ways. The point is to edit the code, predict what it does, then run it and check. A few chapters in the Patterns part have larger exercises, because a pattern shows its value only in a program you build yourself.

When a change breaks the code, read the traceback closely. It underlines the failing part of the line, down to the one operation in a long expression, and ends with a suggestion when you misspell a name, an attribute, or a keyword argument, such as Did you mean: 'count'? for a name.

Each chapter’s Exercises section links to that chapter’s solutions. On the website they are a page beside the chapter. The EPUB and PDF link to the Solutions/ directory of the source repository.

Each solution opens in steps, so you can take as little help as you need: a hint first, then a check that tells you when your attempt is finished, then the full answer. Most solutions add a step after the hint, the shape of the code with its function bodies left out. The full answer closes by asking you to name what separates your attempt from it. tip hint CH=30 N=3 prints the same steps one per run, CH naming the chapter and N the exercise.

Try the exercise yourself before reading the solution. The value is in the prediction and the surprise when you are wrong, not in the code you produce.

If you keep an AI assistant open while you work, the repository’s AGENTS.md asks it to coach you toward your own answer until you open the solution, then to review your answer against the criteria the exercise’s chapter states.

Resources

This book is freely readable at thinkinginpython.com, which always holds the current version.

Other resources:

  • The official Python tutorial
  • The Python Programming FAQ
  • What’s New in Python 3.15, the release notes for the version this book targets
  • The Python type system specification, the reference that defines the annotations the book uses throughout
  • pytest, the test framework the book uses
  • Python Bytes, podcast and newsletter
  • Planet Python, an aggregator of Python articles from around the web

Copyright

© 2026 Bruce Eckel. This book carries a CC BY-NC-ND 4.0 license. You may share it unchanged, with attribution, for noncommercial use. It is freely readable online. Commercial publication requires a contract with me. The source repository’s LICENSE.md has the details.

You are visitor 01402520 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.