
I write Python every day, and like most working developers I learned it by doing things and shipping things rather than by reading a book front to back. That gets you to fluent-enough quickly and it also bakes in a lot of habits from whichever language you wrote first. Fluent Python is the book I picked up the moment I noticed that pattern in my own code, and it is now the reference I keep open next to every Exercism session and every tricky piece of production Python. Below is the honest review of why it is the most trusted Python book on my shelf.
Fluent Python is Luciano Ramalho's now-classic O'Reilly book on writing idiomatic, modern Python 3. The second edition shipped in 2022, was substantially rewritten and expanded - around 1000 pages, 25 chapters across six parts - and updated for Python 3.10+: new dict syntax, structural pattern matching, type hints throughout, modern async/await, the new generic syntax. It is not a 'learn Python' book and it does not pretend to be. It is the book you reach for once you can already write Python and you start to suspect your code has a strong accent from whatever language you wrote first - because, for most of us, it does. Where the official tutorial teaches the syntax, Ramalho teaches the *taste*.

I write Python every day - mostly Django and FastAPI at work, smaller scripts and AI pipelines on the side - and I learned Python the way most working developers learn a second or third language: by doing things, looking up syntax, and shipping. That gets you to fluent-enough surprisingly quickly, and it also bakes in a lot of habits from whatever language you came from. I came from JavaScript, and for a long time my Python *worked* but read like JavaScript with a different syntax. Fluent Python is the book I picked up the moment I noticed that pattern in my own code. It is also the book I now keep open next to every Exercism Python session - because the small puzzles surface exactly the kind of language nuance Ramalho is best at explaining.
Luciano Ramalho is a Python Software Foundation Fellow, has been writing Python since 1998, and has spent more than a decade teaching it - at Globo.com, at his own training company Python.pro.br, and at conferences across the world. He is not a vendor, not a framework author selling you on a stack, not an influencer chasing a trend. He is a teacher who happens to also be a serious working Python programmer, and the book reads exactly like that. Every chapter is dense with references to PEPs, to CPython source, to other people's books and talks - and every one of those references is *honest*: when something is genuinely contested in the community, he says so; when something is his opinion, he labels it 'Soapbox' and puts it at the end of the chapter so you can take it or leave it. That intellectual honesty is what keeps me trusting him.
The book is split into six parts, and Ramalho is explicit in the preface that each part is meant to be read in sequence but the parts themselves can be read in any order. Part I is the prologue - one chapter on the Python data model, which is the philosophical spine of everything that follows. Part II is data structures: sequences, dicts, sets, str-vs-bytes (Unicode!), data classes, and references/mutability. Part III is functions as first-class objects, type hints in functions, decorators and closures, and design patterns done with first-class functions. Part IV is object-oriented idioms: building your own sequences, interfaces and protocols, inheritance, operator overloading, and more type hints in classes. Part V is control flow: iterators and generators, context managers, coroutines and async/await, concurrency models. Part VI is metaprogramming: dynamic attributes and properties, attribute descriptors, and class metaprogramming. There is no filler - every chapter teaches something most working Python programmers do not actually know.
If you only ever read one chapter of Fluent Python, make it Chapter 1. The Python Data Model is the realisation that almost everything in Python - `len(x)`, `x[0]`, `for item in x`, `x + y`, `x in y`, `bool(x)`, `repr(x)`, `with x as ...`, even `async for` - is the language calling a special method (`__len__`, `__getitem__`, `__iter__`, `__add__`, `__contains__`, `__bool__`, `__repr__`, `__enter__`, `__aiter__`) on the object. Once you see this, Python stops feeling like a bag of features and starts feeling like a single, beautifully consistent system. Your own classes can plug into the same protocols the built-ins use, and suddenly all the language's syntax sugar is available to your types for free. Every other chapter in the book is, in some sense, an elaboration of this one idea. It is the closest thing Python has to a unifying theory.
I do Exercism almost every day - small Python exercises, twenty minutes, just me and the test suite. The point of those exercises is not to ship anything; it is to stay in direct contact with the language itself, the part of my brain that is otherwise on autopilot when I am building features at work. And the moment something on Exercism stops being about logic and starts being about *Python* - 'what is the most idiomatic way to do this,' 'is there a stdlib helper I am missing,' 'why does this comprehension feel awkward' - Fluent Python is exactly the right reference. After I solve an exercise I open the relevant chapter and check whether my solution is *Pythonic* or just *correct*. Then I read the community solutions on Exercism. Often the cleanest community solution is doing exactly what Ramalho describes one chapter deeper than where I stopped. That feedback loop - small puzzle, working solution, then a chapter of Fluent Python, then a re-read of the community solutions - has done more for my Python than any other practice I have tried.
Beyond the Exercism habit, this is the book I reach for whenever I hit a hard language problem at work. A subtle bug with mutable default arguments? Chapter 6 on references and mutability. A confusing `==` vs `is` failure? Same chapter. An `__init_subclass__` trick I half-remember? Part VI. Async code that deadlocks under load? Part V, the concurrency chapters - which are some of the clearest explanations of Python's async model I have ever read, full stop. Unicode mangling some user input? Chapter 4 on text-versus-bytes, which is genuinely the best treatment of Python's str/bytes split I have seen in any book. The point is not that I remember every chapter - I do not - but that I *know* the right chapter exists, and I know exactly which part of the book to open. That is the real value of a reference: it converts 'I have a vague feeling Python can do this better' into 'I know where the answer is in 30 seconds.'
The second edition's biggest single change is that type hints are woven through the entire book. There is a dedicated chapter on type hints in functions (Chapter 8) and another on more type hints in classes (Chapter 15), and on top of that almost every other chapter casually annotates its examples and explains why. This was the chapter sequence that finally turned type hints from 'a nice-to-have I sprinkle on my code' into 'a coherent system I can reason about.' Gradual typing, `Optional` vs `| None`, `Protocol` vs ABC, `TypeVar` and bounded generics, `Callable`, `NoReturn`, the difference between *nominal* and *structural* subtyping - it is all here, with worked examples and clear opinions about when type hints help and when they get in the way. Combined with strict mypy, this chapter genuinely changed how I write Python.
Part V is the part I keep coming back to and recommending to other people. It walks the full landscape: classic iterators, generator functions and `yield`, coroutines as generators, native coroutines with `async`/`await`, the event loop, asyncio primitives, threads vs processes, the GIL and where it actually bites, and how to use thread pools and process pools without shooting yourself in the foot. What makes it land is that Ramalho draws the bigger picture *first* - what concurrency model are we even talking about, what is the difference between concurrency and parallelism in Python, why does asyncio look the way it does - before getting into syntax. By the end of the part you can read any async Python codebase and locate yourself: 'this is single-threaded asyncio,' 'this is asyncio with a thread pool for blocking calls,' 'this is multiprocessing for CPU work.' That mental map is rare and it is exactly what most production async bugs come down to.
Every chapter ends with a 'Soapbox' section where Ramalho says what he actually thinks. He calls them out so you can skip them if you want, but I never do - they are some of the best pages in the book. He will tell you, plainly, that he thinks `match` is overrated for some uses, that mutable default arguments are a language flaw, that he is not sure type hints earn their cost in every codebase, that the 'data class as a code smell' framing is honest about a real trade-off. None of this is preached, all of it is labeled, and it is exactly the kind of opinionated-but-clearly-marked content that is missing from most reference books. It also serves as a model for how to write a tech book that ages well: the facts are facts, the opinions are opinions, and you the reader get to pick.
Same playbook as the other big books in my library - Fluent Python lives as an EPUB in BookFusion, on the Boox Palma when I am away from the desk and in the browser when I am at it, and I treat it as a *reference*, not a cover-to-cover read. The honest truth is that I have not finished it linearly and I do not think it wants to be read that way - 1000 pages of dense, opinionated Python is not a beach read. Instead I have a stable rotation: every Exercism session is a candidate to open the relevant chapter; every weird Python bug at work earns a chapter re-read; every time I touch async code I re-read at least one chapter from Part V before I write a line; every time I refactor a class-heavy module I skim Part IV. The companion site at fluentpython.com and the example code on GitHub are excellent for jumping straight to a runnable snippet when I want to verify behaviour rather than re-read prose.
Two honest caveats. First, this is not a beginner book, and Ramalho says so himself in the preface - if you are still learning Python, start with the official tutorial or 'Python Crash Course' and come back to Fluent Python in six months. Read too early, it can give you the impression that every Python script should leverage descriptors and metaclasses, which is the opposite of the lesson. Second, it is genuinely long. 1000 pages of small print. If you try to read it linearly you will bounce; if you treat it as a structured reference and read chapters when the question matches, you will get a decade of value out of it. Pick the second strategy.
Any working Python programmer who has been writing Python for at least a year and is starting to suspect their code has an accent from another language. Mid-level Python engineers will get the biggest single jump - half of what is in this book is not in the official tutorial and is rarely covered in framework-specific docs. Senior engineers and tech leads get a calibration check, a shared vocabulary for code review, and a definitive reference to point teammates at when 'why are we doing it this way' comes up. Anyone using Exercism, Advent of Code, or daily coding katas in Python should keep this book within arm's reach - it is the highest-leverage companion to that kind of practice I know. And anyone shipping serious async or library code in Python should have already read Part V at least once.
Fluent Python is the Python book I trust the most. Ramalho built something rare - a 1000-page reference that is opinionated without being preachy, comprehensive without being a slog, and updated for modern Python without losing the philosophical spine that made the first edition great. I am not pretending to have read every page in order; I have read the chapters that matched what I was building, and I keep coming back for the next one when the next question shows up. Paired with daily Exercism, it is the most reliable way I know to keep getting better at the language itself, not just at building things with it. If a hard Python problem ever stops me cold, this is the first place I look - and almost always the only place I need to.