Dive Into Python 3 PDF Download – Mark Pilgrim (Python 3.1, 2011)

BSCS and BSIT students can download “Dive Into Python 3” by Mark Pilgrim, the free Python 3 book that was for years the standard recommendation for programmers moving from another language into Python. The complete book is free to read online or download as a PDF from its official source.

Read it for what it is. Pilgrim finished the book in 2011 and it targets Python 3.1, so parts of it — installation, packaging, and the two chapters about porting Python 2 code — describe tools that Python has since removed. What has not aged is the core: datatypes, comprehensions, Unicode strings, regular expressions, closures and generators, iterators, unit testing and special methods are explained here better than in most current books, and that material is still correct. The chapter list below marks exactly which chapters are still worth your time and which are not.

Book Overview

CourseProgramming Fundamentals / Python Programming
Degree ProgramsBSCS, BSIT
LevelUniversity — assumes you can already program in some language
Python VersionPython 3.1 — the book was completed in 2011 and has not been updated since
AuthorMark Pilgrim
Copyright2001–2011 Mark Pilgrim
LanguageEnglish
Total Chapters20 chapters and appendices
LicenseCC BY-SA 3.0 — Model: Link-only
FormatFree online HTML and free PDF

Chapter List

Chapter 1: What’s New in “Dive Into Python 3”

Difficulty: Easy · Key topics: who the book is for, differences from the original Dive Into Python

The book opens by telling you plainly who it is not for. Pilgrim addresses readers who already program, and specifically readers of his earlier book “Dive Into Python”, asking whether they are ready to move to Python 3 — and adds that if none of that describes you, “you’d be better off starting at the beginning.” He nicknames this chapter “the minus level”, which is a genuine hint about the book’s approach: it starts above zero and assumes you can already read code. The chapter then previews what changed between Python 2 and Python 3 and points at the 2to3 tool, advice that made sense in 2011 and no longer does.

Key Points:

  • The book is written for people who already program in some language, not for absolute beginners.
  • Pilgrim calls this chapter “the minus level” because it sits before Chapter 0.
  • It is aimed particularly at readers of the original Python 2 “Dive Into Python”.
  • Its advice to “learn, love and use” the 2to3 tool is now obsolete — 2to3 was removed from Python in 3.13.

Common Mistake: Starting here if Python is your first language. The book says so itself in this very chapter; a complete beginner should use a book written for beginners instead.

Important Questions:

  • Who is Dive Into Python 3 written for? Programmers who already know another language and want to learn Python 3 quickly, especially those who had read the earlier Python 2 edition.
  • Why is this called “the minus level”? It is the author’s nickname for an introductory chapter that comes before Chapter 0, signalling that the book starts above the true beginner level.

Chapter 2: Installing Python

Difficulty: Easy · Key topics: installing Python, the Python shell, IDLE, editors and IDEs — this chapter is out of date

This chapter walks through installing Python on Windows, Mac OS X, Ubuntu and other platforms, then introduces the interactive Python shell and IDLE. The problem is the version: it tells you to download the “Python 3.1 Mac Installer Disk Image”, discusses Windows XP, Vista and Windows 7, and describes a time when most Linux distributions still shipped Python 2 by default. Python has moved on by more than a decade of releases since. Skip this chapter and install the current Python from python.org instead; nothing in the rest of the book depends on the steps described here.

Key Points:

  • Do not follow this chapter — it installs Python 3.1, released in 2009.
  • Install the current Python 3 from python.org; the rest of the book still works on it.
  • The chapter’s one lasting piece of advice is that a beginner should get comfortable with the interactive shell early.
  • IDLE ships with Python and is still available, though most people now use VS Code, PyCharm or a similar editor.

Common Mistake: Actually installing Python 3.1 because the book says to. Almost nothing written for Python today runs on it, and modern syntax such as f-strings will fail immediately.

Important Questions:

  • Which Python version does this book use? Python 3.1, released in 2009. Install the current version instead; the language fundamentals the book teaches are unchanged.
  • What is IDLE? The simple editor and interactive shell that ships with Python, useful for trying short snippets while learning.

Chapter 3: Your First Python Program

Difficulty: Easy · Key topics: declaring functions, docstrings, the import search path, everything is an object, indentation, exceptions, running scripts

Pilgrim refuses the usual slow build-up. He opens with a complete working program and says you will dissect it line by line, and that is exactly what the chapter does. From that one script he draws out how functions are declared with def and need no return type, how optional and named arguments work, why docstrings matter, how sys.path controls where imports are found, and the central claim that everything in Python is an object with attributes and methods — including functions themselves. Indentation as the only block delimiter, exceptions as normal rather than exceptional, the NameError you get from an unassigned variable, case sensitivity, and the if __name__ == '__main__' block all follow from that single example.

Key Points:

  • A function is declared with def, a name, and arguments in parentheses — no return type is declared.
  • Everything in Python is an object; every function has a built-in __doc__ attribute holding its docstring.
  • Python looks for imported modules in every directory listed in sys.path, which is an ordinary list you can inspect.
  • Indentation is the only block delimiter — there are no braces and no begin/end keywords.
  • Referencing a variable that was never assigned raises a NameError, not a silent default.
  • Every module has a __name__ attribute, which equals '__main__' only when the file is run directly.

Practice Tip: Type the humansize.py example out by hand rather than downloading it, then break it deliberately — remove an indent, misspell a variable, delete the colon. Reading the resulting error messages teaches more in ten minutes than the chapter’s prose does.

Common Mistake: Treating if __name__ == '__main__' as boilerplate to copy without understanding. It is what lets the same file be both an importable module and a runnable script, and exam questions ask exactly that.

Important Questions:

  • What does if __name__ == '__main__' do? Every module has a __name__ attribute; it equals '__main__' only when the file is run directly, so the block runs on execution but not on import.
  • How does Python mark the beginning and end of a function body? With a colon after the declaration and the indentation of the code itself — there are no braces or end keywords.

Chapter 4: Native Datatypes

Difficulty: Easy · Key topics: booleans, integers and floats, lists, tuples, sets, dictionaries, None, boolean contexts

The most-used chapter in the book, and one that has aged perfectly. Python has no type declarations: every value has a datatype, and Python infers and tracks it from the assignment. The chapter takes each native type in turn. Booleans and the idea of a boolean context, where almost any expression can stand in for True or False. Numbers, where the presence of a decimal point is the only thing distinguishing an int from a float. Lists, described as Python’s workhorse, with creating, slicing, appending, searching and removing each shown at the shell. Tuples as immutable lists that simply lack the mutating methods. Sets as unordered bags of unique values supporting union, intersection and difference. Dictionaries as unordered key-value pairs optimised for lookup by key. And None, which is not False, not 0 and not an empty string.

Key Points:

  • You never declare a variable’s type — Python infers it from the assignment and tracks it internally.
  • A tuple is an immutable list: it simply has no append(), insert(), remove() or pop().
  • A set is an unordered collection of unique values and supports union, intersection and difference.
  • A dictionary is optimised for retrieving a value when you know the key, not the other way round.
  • None is the only null value and has its own type, NoneType.
  • Comparing None to anything other than None always returns False.
  • Almost any expression can be used in a boolean context, and every type defines what counts as false.

Memory Tip: Sort the four collections by two questions: is it ordered, and is it changeable? List is ordered and mutable, tuple is ordered and immutable, set is unordered with unique values, dictionary is key-to-value. Answer those two questions and you have picked the right structure.

Common Mistake: Writing if x == None. Use if x is NoneNone is a single object, so identity is the correct test, and the book’s point that all variables holding None are equal to each other is exactly why.

Important Questions:

  • What is the difference between a list and a tuple? Both are ordered sequences, but a tuple is immutable — it cannot be changed after creation and has none of the methods that modify a list.
  • What is None in Python? The single null value, with its own type NoneType. It is not False, not 0 and not an empty string, and comparing it to anything else returns False.

Chapter 5: Comprehensions

Difficulty: Easy · Key topics: the os module, working with paths, list comprehensions, dictionary comprehensions, set comprehensions

Pilgrim opens with a good observation: every language has one feature it deliberately made simple, and if you come from another language you can easily miss it because your old language made something else simple instead. In Python that feature is the comprehension. The chapter first builds the raw material by touring the os module — the current working directory, splitting and joining filenames, listing directories, reading file metadata, constructing absolute paths — and then shows comprehensions operating on those results. A list comprehension maps one list into another by applying an expression to each element, and can filter with an if clause so the result is smaller than the input. Dictionary and set comprehensions follow the same syntax, including the trick of swapping a dictionary’s keys and values.

Key Points:

  • A list comprehension maps a list into a new list by applying an expression to each element.
  • An if clause inside a comprehension filters, so the output can be shorter than the input.
  • Dictionary comprehensions build a dictionary and use key:value pairs; set comprehensions build a set and use single values.
  • The only syntactic difference between a dictionary and a set comprehension is the key:value pair.
  • The os module offers one API across operating systems so the same code runs on Windows, Mac and Linux.
  • Any Python expression can appear inside a comprehension, including function calls.

Practice Tip: Take three loops you have already written that build a list and rewrite each as a comprehension. Comprehensions are the single most recognisable feature of idiomatic Python, and converting your own code is how the syntax stops looking strange.

Common Mistake: Cramming several operations and nested conditions into one comprehension. If it no longer fits on one readable line, a plain loop is the better answer — comprehensions are for clarity, not for showing off.

Important Questions:

  • What is a list comprehension? A compact way to build a new list by applying an expression to each element of an existing sequence, optionally filtering with an if clause.
  • How does a dictionary comprehension differ from a set comprehension? The syntax is the same except that a dictionary comprehension produces key:value pairs while a set comprehension produces single values.

Chapter 6: Strings

Difficulty: Medium · Key topics: Unicode, character encodings, formatting strings, string methods, slicing, strings versus bytes, source file encoding

This is the chapter that made the book’s reputation, and it is still one of the clearest explanations of Unicode in any programming text. It opens with why text is hard — alphabets ranging from the twelve letters of Rotokas to the thousands of characters in Chinese, Japanese and Korean — and how Unicode assigns every character from every language a unique number. Then it states Python 3’s central rule: all strings are sequences of Unicode characters, so “is this string UTF-8?” is an invalid question, because UTF-8 is a way of encoding characters as bytes, not a property of a string. The rest covers the format() method with positional and compound field names and format specifiers, common string methods, slicing, and the strict separation of str from bytes, bridged only by encode() and decode().

Key Points:

  • In Python 3 every string is a sequence of Unicode characters — a string has no encoding.
  • An encoding such as UTF-8 is a way of turning characters into bytes; it is a property of bytes, not of strings.
  • A bytes object is an immutable sequence of numbers between 0 and 255.
  • bytes.decode(encoding) gives a string; str.encode(encoding) gives bytes. Nothing else crosses the line.
  • You can never mix bytes and strings in the same operation.
  • Python 3 assumes every .py source file is UTF-8 unless an encoding declaration says otherwise.
  • The chapter teaches format(); f-strings did not exist in 2011 and are not covered.

Memory Tip: Hold on to Pilgrim’s own line: bytes are bytes, characters are an abstraction. Decode takes you from the machine’s bytes up to human characters, encode pushes you back down. Once the direction is fixed in your head, UnicodeDecodeError stops being mysterious.

Common Mistake: Asking what encoding a Python 3 string is in. The question has no answer — strings hold characters, and only bytes have an encoding. Almost every Unicode bug in beginner code comes from blurring that line.

Important Questions:

  • What is the difference between a string and a bytes object in Python 3? A string is an immutable sequence of Unicode characters; a bytes object is an immutable sequence of numbers between 0 and 255. They cannot be mixed.
  • How do you convert between strings and bytes? Call encode() on a string with a character encoding to get bytes, and decode() on bytes with the same encoding to get a string back.

Chapter 7: Regular Expressions

Difficulty: Medium · Key topics: the re module, case studies on street addresses and Roman numerals, the {n,m} syntax, verbose regular expressions, capture groups

The chapter starts by explaining when not to use regular expressions: Python strings already have index(), find(), split(), count() and replace(), and those handle the simple cases fine. Regular expressions earn their complexity only when the pattern itself is complex. Three real case studies follow, all drawn from problems Pilgrim actually had at work: standardising street addresses exported from a legacy system, validating Roman numerals, and parsing American phone numbers. The Roman numeral case builds a pattern piece by piece — thousands, then hundreds, then tens and ones — and introduces the {n,m} repetition syntax as a more readable alternative. Verbose regular expressions, which allow whitespace and inline comments, close the chapter, along with capture groups for pulling out the pieces that matched.

Key Points:

  • Use plain string methods for simple cases; regular expressions are for genuinely complex patterns.
  • {n,m} matches between n and m repetitions and is usually more readable than repeating a character.
  • A verbose regular expression ignores whitespace and allows comments, so a complex pattern can be documented inline.
  • Capture groups let you extract the specific pieces that matched, not just test whether the whole pattern matched.
  • The Roman numeral pattern is built up place value by place value rather than written in one go.
  • The chapter’s own warning: regular expressions are powerful but are not the right solution to every problem.

Practice Tip: Build your patterns in the interactive shell one piece at a time, exactly as the Roman numeral case study does, testing after each addition. Writing a long regular expression in one attempt and then debugging it is far harder than growing it in steps.

Common Mistake: Reaching for a regular expression when str.startswith(), in or split() would do. The chapter opens by warning against this, and unnecessary regular expressions are the usual source of unreadable Python.

Important Questions:

  • When should you use a regular expression instead of a string method? When the pattern is genuinely complex — string methods such as find() and replace() handle single fixed substrings and are simpler and faster for those.
  • What is a verbose regular expression? A pattern written with the verbose flag, so whitespace is ignored and comments can be included, making a complex expression readable and documentable.

Chapter 8: Closures & Generators

Difficulty: Medium · Key topics: functions as objects, a list of functions, a list of patterns, a file of patterns, generators, yield

The chapter builds one program — an English pluralisation function — and refactors it five times, each version introducing a new idea. The first uses regular expression substitutions directly. The second stores the rules as a list of functions, which works because everything in Python is an object, functions included, so a data structure can contain the functions themselves rather than their names. The third factors out the repeated pattern, since every match function calls re.search() and every apply function calls re.sub(). The fourth moves the rules out into a plain text file so they can be maintained separately from the code. The fifth introduces generators: a function that uses yield produces values one at a time and remembers where it left off, so the rules file can be read lazily.

Key Points:

  • Functions are objects, so a list or dictionary can hold the functions themselves, not just their names.
  • A closure is a function that remembers values from the scope in which it was created.
  • A generator is a function containing yield; calling it returns a generator object without running the body.
  • Execution resumes after the yield each time the next value is requested, with all local state intact.
  • Generators produce values lazily, so an infinite sequence such as Fibonacci can be written naturally.
  • The chapter refactors the same program five times — the progression is the lesson, not the final version.

Memory Tip: The difference between return and yield is memory. return ends the function and forgets everything; yield hands back one value and freezes the function exactly where it stands, ready to continue. That one word is the whole concept.

Common Mistake: Expecting a generator function to run when you call it. Calling it only creates the generator object — no code in the body executes until you ask for the first value.

Important Questions:

  • What is a generator? A function containing yield that produces values one at a time, pausing after each and resuming with its local state intact when the next value is requested.
  • What is the difference between return and yield? return ends the function and discards its state; yield produces one value and suspends the function so it can continue from that point later.

Chapter 9: Classes & Iterators

Difficulty: Medium · Key topics: defining classes, __init__, instantiating, instance variables, the iterator protocol, __iter__ and __next__

Pilgrim calls iterators “the secret sauce of Python 3” and argues that comprehensions and generators are both just simple forms of the same idea. The chapter first covers classes properly: Python is fully object-oriented, a class needs no separate interface definition, and __init__() initialises a new instance rather than constructing it. Instantiating a class means calling it like a function. Instance variables belong to one object, so two instances keep their own values. Then it builds the same Fibonacci sequence twice — once as a generator and once as a class implementing __iter__() and __next__() — and shows the calling code is byte-for-byte identical. That comparison is the point of the chapter: it reveals what a for loop actually does under the hood.

Key Points:

  • A class is defined with the class keyword and needs no separate interface declaration.
  • __init__() runs when an instance is created — it initialises the object rather than constructing it.
  • You instantiate a class by calling it like a function, passing whatever __init__() requires.
  • Instance variables belong to a single object; two instances keep separate values.
  • An iterator is any class defining __iter__() and __next__().
  • A for loop calls __iter__() to get an iterator, then __next__() repeatedly until StopIteration is raised.
  • A generator and an equivalent iterator class are used identically by the calling code.

Memory Tip: Remember what for really is: call __iter__() once, then __next__() over and over until StopIteration. Every loop you have ever written in Python does exactly that, and knowing it makes iterators, generators and comprehensions one idea instead of three.

Common Mistake: Calling __init__() a constructor. It initialises an object that already exists; the real construction happens in __new__(), which you rarely touch.

Important Questions:

  • What makes a class an iterator? Defining __iter__(), which returns the iterator, and __next__(), which returns the next value or raises StopIteration.
  • What does a for loop actually do? It calls __iter__() on the object to get an iterator, then calls __next__() repeatedly, stopping when StopIteration is raised.

Chapter 10: Advanced Iterators

Difficulty: Hard · Key topics: itertools, findall, assert, generator expressions, permutations, str.translate, eval

The whole chapter is built around one puzzle: an alphametic, where letters stand for digits and the words spell an arithmetic equation. Pilgrim solves it by brute force in fourteen lines, and each technique needed is introduced along the way. re.findall() collects every letter in the puzzle. A set finds the unique characters. An assert bails out early if there are more than ten distinct letters, since there are only ten digits, so no solution can exist. Generator expressions appear as generator functions without the function. itertools.permutations() generates every possible digit assignment lazily rather than building them all in memory. The string translate() method — powerful and little known — substitutes the digits into the puzzle, and eval() evaluates the resulting string as a Python expression.

Key Points:

  • A generator expression is a generator function without the function — compact and functionally equivalent.
  • itertools produces values lazily, so permutations are generated one at a time instead of all at once.
  • An assert statement raises AssertionError when its condition is false, and is used here to stop impossible puzzles early.
  • str.translate() maps characters through a translation table in one pass.
  • eval() evaluates a string as a Python expression — including boolean expressions.
  • The whole solver is fourteen lines, made possible by composing these tools rather than writing loops.

Memory Tip: The lesson underneath the puzzle is laziness. itertools never builds the full list of permutations — it hands you one at a time. That is why a brute-force search over millions of possibilities fits in memory at all, and it is the idea behind generators, comprehensions and iterators alike.

Common Mistake: Copying the chapter’s use of eval() into real code. It runs whatever string you give it, so calling it on anything a user supplied is a serious security hole — safe here only because the string is built entirely by the program.

Important Questions:

  • What is a generator expression? A compact expression that produces values lazily, equivalent to a generator function but written inline without def or yield.
  • Why is itertools useful for a brute-force search? It generates possibilities lazily, one at a time, so an enormous search space can be explored without ever holding all of it in memory.

Chapter 11: Unit Testing

Difficulty: Easy · Key topics: test cases, test-driven development, unittest, testing for failure, custom exceptions

This chapter and the next are the strongest pair in the book, and the reason it is still recommended. Pilgrim inverts the usual order: he writes the tests first, for a Roman numeral converter, and only then writes code to make them pass. The chapter states the principle that a test case answers a single question about the code it tests, and then works through the surprising part — that testing success is only half the job. You must also test that the function fails when given bad input, and fails in the specific way you expect. Numbers too large, numbers too small, zero, negatives and non-integers each get their own test and their own custom exception. Every test is shown failing before the code is written, which is the discipline the chapter is really teaching.

Key Points:

  • A test case answers a single question about the code it is testing.
  • Write the test first and watch it fail — a test that has never failed proves nothing.
  • Testing that good input succeeds is only half the work; you must also test that bad input fails.
  • Failure must be specific: the function should raise the exception you expect, not just any error.
  • Defining your own exception classes makes failures precise and testable.
  • A test class needs no __init__(); the framework discovers and runs the test methods itself.

Practice Tip: Follow the Roman numeral example by actually typing it in and running the tests at each stage, including the stages where they fail. Watching a test go red and then green is the entire point, and reading about it does not produce the same understanding.

Common Mistake: Writing tests only for input you know works. Most real bugs live in the error paths, and a suite that never checks failure gives false confidence.

Important Questions:

  • What should a single test case do? Answer one question about the code under test — run independently, verify one behaviour, and report clearly whether it passed or failed.
  • Why test that a function fails? Because correct behaviour on bad input matters as much as correct behaviour on good input, and the function must fail in the specific, documented way callers expect.

Chapter 12: Refactoring

Difficulty: Hard · Key topics: bugs as missing tests, handling changing requirements, refactoring safely, the limits of unit testing

The follow-on to the testing chapter, and it opens with a definition worth remembering: a bug is a test case you have not written yet. So when you find one, you reproduce it, write a failing test that captures it, and only then fix it. The chapter then handles the situation every real project meets — requirements change, because most customers do not know what they want until they see it. Having a full test suite is what makes changing the code safe, and Pilgrim argues that the best thing about comprehensive tests is not the good feeling when they pass but the freedom to refactor mercilessly. The summary is refreshingly honest: unit testing is not a magic problem solver, writing good tests is hard, and keeping them current takes discipline.

Key Points:

  • A bug is a test case you have not written yet.
  • Reproduce the bug, write a failing test for it, then fix the code — in that order.
  • Refactoring means changing how code works internally without changing what it does.
  • A full test suite is what makes refactoring safe, because it tells you immediately if behaviour changed.
  • Requirements will change; tests are how you absorb that without fear.
  • Unit testing is not a silver bullet — good tests are hard to write and take discipline to maintain.

Memory Tip: “A bug is a test case you haven’t written yet” is worth memorising word for word. It converts every bug report into a concrete next action, and it is the sentence most likely to be quoted back at you in a software engineering paper.

Common Mistake: Fixing a bug and moving on without adding a test. Nothing then stops the same bug returning, and the test suite quietly stops reflecting what the code is supposed to do.

Important Questions:

  • What is refactoring? Changing the internal structure of working code to make it clearer or faster without changing its external behaviour.
  • What should you do first when you find a bug? Reproduce it, then write a test case that fails because of it, and only then fix the code — so the bug can never return unnoticed.

Chapter 13: Files

Difficulty: Medium · Key topics: open(), encodings, stream objects, the with statement, reading line by line, writing, binary files, stdin/stdout/stderr

Opening a file in Python is easy, but this chapter insists on the part beginners skip: the encoding argument. Because Python 3 strings are Unicode and files hold bytes, reading a text file means decoding, and if you do not name the encoding Python guesses using a platform default — which is exactly why the same script works on one machine and fails on another. The chapter covers reading whole files and line by line, closing files explicitly and then automatically with the with statement, both write modes, binary files where you get bytes rather than characters, and stream objects generally. It ends with standard input, output and error, and how print() is really just writing to the stdout pipe, which can be redirected.

Key Points:

  • open() returns a stream object; always pass encoding for text files rather than relying on the platform default.
  • A with block closes the file automatically, even if an exception is raised inside it.
  • Opening in text mode gives you strings; opening in binary mode gives you bytes.
  • Both write modes create the file if it does not exist — the difference is whether existing content is truncated or appended to.
  • A stream object is anything with a read() method, so your functions should accept streams rather than filenames.
  • print() writes to the stdout pipe, which can be redirected like any other stream.

Practice Tip: Write a file containing non-English text, then read it back twice — once with the correct encoding and once with the wrong one. Seeing the same bytes turn into different characters makes the encoding argument something you will never omit again.

Common Mistake: Calling open() without encoding and without a with block. The first makes your program behave differently on different machines; the second leaves files open when an exception is raised.

Important Questions:

  • Why should you use a with statement to open files? Because it closes the file automatically when the block ends, including when an exception is raised inside it.
  • Why does open() take an encoding argument? Files store bytes but Python 3 strings are Unicode characters, so reading text means decoding — and without an explicit encoding Python falls back to a platform-dependent default.

Chapter 14: XML

Difficulty: Hard · Key topics: XML structure, Atom feeds, ElementTree, searching nodes, namespaces, lxml, generating XML, broken XML

Unlike every other chapter, this one is about data rather than code, and it uses a real Atom syndication feed as its example throughout — a format with a title, a subtitle, a last-updated date and a list of entries, each with its own title and date. A five-minute crash course covers elements, start and end tags, nesting, attributes and namespaces. Then the chapter works with ElementTree, chosen over the traditional DOM and SAX parsers: an element behaves like a list whose items are its children and like a dictionary whose keys are its attributes. Searching for specific nodes, going further with the third-party lxml library and its XPath support, generating XML from scratch, and the “draconian error handling” the XML specification mandates — parsers must halt on any wellformedness error — complete the chapter.

Key Points:

  • An XML document is a tree of elements delimited by start and end tags and nestable to any depth.
  • In the ElementTree API an element acts like a list of its children and a dictionary of its attributes.
  • find() returns the first matching element; findall() returns every match.
  • The XML specification mandates draconian error handling — a conforming parser must stop at the first wellformedness error.
  • Namespaces qualify element names so documents from different vocabularies can be combined.
  • lxml is a third-party library with a compatible API plus full XPath 1.0 support.

Memory Tip: Remember XML’s defining rule as the opposite of HTML’s. A browser will render almost any broken HTML, but an XML parser must halt at the first error. That single contrast explains why XML feels strict and why so much real-world XML fails to parse.

Common Mistake: Forgetting namespaces when searching an Atom or RSS feed. The element is not title but {http://www.w3.org/2005/Atom}title, and searching without the namespace silently returns nothing.

Important Questions:

  • What is draconian error handling in XML? The specification’s requirement that a conforming parser must stop immediately on any wellformedness error rather than trying to recover.
  • How does ElementTree represent an XML element? As an object that behaves like a list of its child elements and like a dictionary of its attributes.

Chapter 15: Serializing Python Objects

Difficulty: Hard · Key topics: pickle, pickle protocols, pickling to memory, JSON, mapping Python types to JSON, unsupported types

Serialization is saving a data structure so it can be reloaded, reused or sent elsewhere, and the chapter covers the two Python answers. First pickle, which handles almost any Python object and reproduces it exactly on loading — the dump and load cycle gives back a structure equal to the original. It also covers pickling to a bytes object in memory rather than a file, and the four pickle protocol versions, since a file written by a newer protocol cannot be read by older code. Then JSON, for when other languages must read the data, since pickle’s format is Python-specific and makes no attempt at compatibility. The most useful section is the mismatch: JSON has no tuple type and no bytes type, so those two Python types do not survive a round trip unchanged.

Key Points:

  • pickle stores almost any Python object; pickle.dump() and pickle.load() return an equal structure.
  • Pickle files are binary and unreadable by eye; the format is Python-specific by design.
  • There are four pickle protocol versions, so a file written with a newer one may not load in older code.
  • JSON is the choice when another language has to read the data.
  • JSON has no tuple type — a tuple comes back as a list.
  • JSON has no bytes type, so bytes must be encoded some other way before serializing.
  • Never unpickle data from an untrusted source; loading a pickle can execute arbitrary code.

Memory Tip: Choose by audience. If only Python will ever read it back, use pickle and keep every type exactly. If anything else might read it, use JSON and accept that tuples become lists and bytes need handling. One question decides it.

Common Mistake: Assuming a JSON round trip is lossless. Save a tuple and you get a list back — the chapter singles this out, and it causes bugs that appear long after the save.

Important Questions:

  • What is the difference between pickle and JSON? Pickle is a Python-specific binary format that preserves almost any Python object exactly; JSON is a text format readable by other languages but supporting fewer types.
  • Which Python types does JSON not support? Tuples, which come back as lists, and bytes, which have no JSON equivalent at all.

Chapter 16: HTTP Web Services

Difficulty: Hard · Key topics: HTTP verbs, caching, Last-Modified, ETag, compression, redirects — the library it teaches is obsolete

Pilgrim defines HTTP web services in twelve words: exchanging data with remote servers using nothing but the operations of HTTP. GET to read, POST to send, PUT and DELETE for the rest. The chapter’s real value is the five features it says every HTTP client should support, because network access is extraordinarily expensive compared with local work: caching, Last-Modified checking, ETag checking, compression and redirects. It then shows the naive way to fetch a feed, turns on debugging to display what actually goes over the wire, and demonstrates that the naive code is both inefficient and rude — it requests uncompressed data from a server that supports gzip. All of that reasoning is still correct. What is not is the library: the chapter teaches httplib2 and tells you to download it from code.google.com, a site that shut down in 2016. Today the same work is done with requests or httpx.

Key Points:

  • HTTP web services means exchanging data using only HTTP’s own operations: GET, POST, PUT and DELETE.
  • Network access is extremely expensive compared with local computation — the whole chapter follows from that.
  • The five features every HTTP client should support are caching, Last-Modified checking, ETag checking, compression and redirects.
  • A cached copy plus a Last-Modified or ETag check avoids re-downloading data that has not changed.
  • Requesting uncompressed data from a server that supports gzip wastes bandwidth for everyone.
  • Obsolete: the chapter’s library httplib2 is fetched from Google Code, which closed in 2016. Use requests or httpx instead.

Memory Tip: Read this chapter for the five client features and ignore every code sample. The concepts — caching, conditional requests, compression, redirects — are exactly the same in requests today; only the function names changed.

Common Mistake: Trying to install httplib2 from the URL in the chapter. The site no longer exists; install requests with pip and map each example onto it as you read.

Important Questions:

  • What five features should an HTTP client support? Caching, Last-Modified checking, ETag checking, compression and following redirects.
  • Why do caching and conditional requests matter so much? Because opening a connection and retrieving a response is far slower than any local work, so the cheapest request is the one you never have to make.

Chapter 17: Case Study — Porting chardet to Python 3

Difficulty: Hard · Key topics: character encoding auto-detection, the chardet module, running 2to3 — this chapter is obsolete

The opening question is a good one: what is the number one cause of gibberish text on the web and in your inbox? Character encoding. The chapter explains encoding auto-detection — taking bytes in an unknown encoding and working out what it is, “like cracking a code when you don’t have the decryption key” — and why it is possible at all: languages are not random, so a computer can study typical text and make an educated guess, much as an English reader instantly recognises that “txzqJv 2!dasd0a” is not English. That explanation is still worth reading. The rest of the chapter is not: it runs the 2to3 tool over the Python 2 version of chardet and then fixes what 2to3 could not, error by error. Python 2 reached end of life in 2020 and the 2to3 tool was removed from Python in 3.13, so this exercise cannot be reproduced today.

Key Points:

  • Character encoding is the single biggest cause of garbled text on the web.
  • Encoding auto-detection means inferring an unknown encoding from the bytes alone.
  • It works because languages are not random — some character sequences are common and others never occur.
  • The chardet module implements this and is still available and maintained today.
  • Obsolete: the porting exercise depends on the 2to3 tool, removed from Python in 3.13.
  • Obsolete: Python 2 reached end of life in January 2020, so porting from it is no longer a live problem.

Common Mistake: Working through this chapter as a tutorial. Read the first two sections for the encoding-detection explanation, then stop — the remaining sections solve a problem that no longer exists with a tool that no longer ships.

Important Questions:

  • What is character encoding auto-detection? Taking a sequence of bytes whose encoding is unknown and inferring the encoding from the byte patterns, so the text can be read correctly.
  • Why is auto-detection possible at all? Because languages are not random: certain character sequences occur constantly in a given encoding and others never occur, so an algorithm can make an educated guess from statistics.

Chapter 18: Packaging Python Libraries

Difficulty: Hard · Key topics: Distutils, setup.py, package classifiers, manifests, source distributions, PyPI — this chapter is obsolete

The chapter teaches how to release your own Python code, using the packaging framework that shipped with Python 3 at the time: Distutils. It presents Distutils as several things at once — a build tool for the author, an installation tool for users, and a package metadata format for search engines — all centred on a setup script traditionally named setup.py, and integrating with the Python Package Index. It covers directory structure, writing and error-checking the setup script, classifying a package, listing extra files with a manifest, building a source distribution and a graphical installer, and uploading to PyPI. Distutils was deprecated in Python 3.10 and removed entirely in Python 3.12, so none of the mechanics here apply now. Modern packaging uses a pyproject.toml file with a build backend such as setuptools, Hatch or Poetry.

Key Points:

  • The chapter’s whole subject, Distutils, was removed from Python in 3.12 — the mechanics no longer work.
  • Modern Python packaging uses pyproject.toml with a build backend, not setup.py with Distutils.
  • The chapter’s closing section is titled “The Many Possible Futures of Python Packaging” — those futures have since arrived.
  • What survives: the ideas of package metadata, classifiers, a manifest for extra files, and a source distribution.
  • PyPI is still the central repository for open source Python libraries.

Common Mistake: Following this chapter to publish a package. Every command it gives will fail on a current Python. Read the Python Packaging User Guide instead, and use this chapter only for the general shape of what a package contains.

Important Questions:

  • Is the packaging method in this chapter still usable? No. It is built on Distutils, which was deprecated in Python 3.10 and removed in Python 3.12; current packaging uses pyproject.toml with a build backend.
  • What is PyPI? The Python Package Index, the central public repository from which packages are installed — still in use today.

Chapter 19: Porting Code to Python 3 with 2to3

Difficulty: Hard · Key topics: a reference of every Python 2 to Python 3 change — this chapter is obsolete

The longest chapter in the book, and now the least useful. It is a reference of everything the 2to3 tool converts automatically, listed change by change: print becoming a function, the merging of Python 2’s two string types into one Unicode string type, the removal of unicode(), long, has_key() and the <> operator, the renaming and reorganisation of standard library modules such as urllib and http, and dozens more. It is a genuinely thorough catalogue and was valuable in 2011. It has no use now: Python 2 reached end of life in January 2020, and the 2to3 tool itself was deprecated in Python 3.11 and removed in Python 3.13. There is no Python 2 code left in a normal university course and no tool left to run.

Key Points:

  • The chapter documents what the 2to3 tool converts automatically from Python 2 to Python 3.
  • Obsolete: 2to3 was deprecated in Python 3.11 and removed in Python 3.13.
  • Obsolete: Python 2 reached end of life in January 2020.
  • One fact worth keeping: Python 2 had two string types, Unicode and non-Unicode; Python 3 has one, and it is Unicode.
  • Another: print was a statement in Python 2 and is a function in Python 3.
  • Skip the remaining forty-odd sections unless you are maintaining genuinely ancient code.

Common Mistake: Studying this chapter for an exam. Python 2 versus Python 3 differences occasionally appear in older papers, but the two facts above cover almost all of it — the rest is a tool reference for a tool that no longer exists.

Important Questions:

  • What is the main string difference between Python 2 and Python 3? Python 2 had two string types, Unicode and non-Unicode; Python 3 has one string type and it is always Unicode.
  • Is the 2to3 tool still available? No. It was deprecated in Python 3.11 and removed in Python 3.13, and Python 2 itself reached end of life in 2020.

Chapter 20: Special Method Names

Difficulty: Hard · Key topics: dunder methods, making classes act like iterators, functions, sets, dictionaries and numbers, comparison, context managers

The book’s most useful reference chapter, and one that has aged perfectly. It collects the “magic” methods Python calls when you use particular syntax, and organises them by what you are trying to make your class behave like. Basics first, including __init__() and the methods that help when debugging a custom class. Then classes that act like iterators through __iter__() and __next__(). Computed attributes through __getattr__() and __getattribute__(), with a careful warning that the second is absolute and unconditional and is also called when Python looks up a method name. Classes that act like functions through __call__(), like sets through __contains__() and __len__(), like dictionaries, like numbers, and classes that can be compared or used in a with block. Real standard-library examples — zipfile, cgi, Fraction — show each one in use.

Key Points:

  • Special methods are what Python calls behind the syntax — len(x) calls x.__len__(), a + b calls a.__add__(b).
  • Defining __iter__() and __next__() makes your class usable in a for loop.
  • __call__() makes an instance callable like a function, useful when an operation needs to carry state.
  • __getattr__() is called only when normal lookup fails; __getattribute__() is called on every attribute access, including method lookups.
  • __contains__() and __len__() make a class respond to in and len().
  • __enter__() and __exit__() make a class usable in a with block.
  • The chapter is written as a reference to return to, not a chapter to read once.

Memory Tip: Do not memorise the list. Learn the pattern instead: every piece of Python syntax has a dunder method behind it, so ask “what method does this syntax call?” and look it up here. That one habit replaces the whole table.

Common Mistake: Overriding __getattribute__() when __getattr__() was meant. __getattribute__() intercepts every attribute access including method lookups, so a small mistake there breaks the class completely — the chapter warns about exactly this.

Important Questions:

  • What is the difference between __getattr__() and __getattribute__()? __getattr__() runs only when normal attribute lookup fails; __getattribute__() runs on every attribute access, including method lookups, so it must be used with great care.
  • Which special methods let a class be used in a with block? __enter__(), which runs on entering the block, and __exit__(), which runs on leaving it including when an exception occurs.

Download Dive Into Python 3 PDF (Free)

This book is free from its official source. Click below to open the official site, where the complete book can be read online chapter by chapter or downloaded as a PDF.

↓ Download PDF

How to Study This Book

Read Chapters 3 to 15 and Chapter 20. That is the part of the book that is still correct and still excellent, and it covers the whole Python language: functions and objects, native datatypes, comprehensions, Unicode strings, regular expressions, closures and generators, classes and iterators, unit testing, refactoring, files, XML, serialization and special methods.

Skip Chapter 2 entirely and install the current Python from python.org instead. Everything else in the book runs on it without changes.

Skip Chapters 17, 18 and 19. They are about the 2to3 tool and Distutils, both of which Python has since removed, and about porting from Python 2, which ended in 2020. Chapter 17’s first two sections on character encoding detection are worth reading on their own; stop when the chapter starts running 2to3.

Read Chapter 16 for its ideas but not its code. Caching, conditional requests, compression and redirects are exactly as important now as they were then; the httplib2 library it uses is not, so map the examples onto requests as you read.

Chapters 11 and 12 on unit testing and refactoring are the strongest in the book and the ones most likely to help you in a Software Engineering paper as well as in Programming. Do them by actually typing the Roman numeral example and running the tests at each stage.

Fill the gaps afterwards. The book predates f-strings, type hints, dataclasses, async and await, pathlib, the walrus operator and match statements — all of which a current course will expect. Read the official Python documentation or a current book for those, and use Dive Into Python 3 for the fundamentals it explains so well.

If Python is your first programming language, start somewhere else. This book says so itself in its opening chapter: it is written for people who already program. Think Python is the better starting point, and it is current.


Used In These Programs

This book is used as supporting reading for Python programming in: BS Computer Science · BS Information Technology. Browse all Python books or all Computer Science books.

Who Should Read This

Dive Into Python 3 suits a student who already programs in C++, Java or another language and wants to pick up Python quickly, which is the situation most BSCS students are in by their third or fourth semester. Its explanation of Unicode and of Python’s separation of strings from bytes is still among the clearest available anywhere, and its two chapters on unit testing and refactoring are genuinely excellent. It is not a first programming book, and the author says so himself in the opening chapter. It is also not a current one: it targets Python 3.1 from 2011, so treat it as a strong explanation of Python’s fundamentals rather than an up-to-date guide, and pair it with the official documentation for everything the language has gained since. If you are learning to program for the first time, or you need a book that matches today’s Python, use Think Python instead.


Applicable Universities

This book is useful for students at Pakistani universities offering BSCS and BSIT including Punjab University, Virtual University, COMSATS, FAST, UET, NUST, and other HEC-recognized institutions.

FAQs

Is Dive Into Python 3 free?

Yes. Mark Pilgrim released it under the Creative Commons Attribution-ShareAlike 3.0 licence, and the complete book is free to read online or download as a PDF.

Which Python version does Dive Into Python 3 use?

Python 3.1, from 2009. The book was completed in 2011 and has not been updated since, so install the current Python from python.org rather than the version its installation chapter describes.

Is Dive Into Python 3 still worth reading in 2026?

Parts of it, yes. The chapters on datatypes, comprehensions, Unicode strings, regular expressions, generators, iterators, unit testing and special methods are still correct and unusually clear. The chapters on installation, packaging and porting from Python 2 are obsolete — the tools they teach have been removed from Python.

Which chapters should I skip?

Skip Chapter 2 (it installs Python 3.1), Chapter 18 (Distutils, removed in Python 3.12) and Chapter 19 (the 2to3 tool, removed in Python 3.13). Read only the first two sections of Chapter 17, and read Chapter 16 for its concepts rather than its code.

Is this book good for a complete beginner?

No, and the book says so in its own opening chapter — it is written for people who already program in another language. A first-time programmer should start with Think Python instead.

What modern Python features does the book not cover?

It predates f-strings, type hints, dataclasses, async and await, pathlib, the walrus operator and match statements. Use the official Python documentation for those.

Related Books

Dive Into Python 3 is worth keeping for the chapters that have not aged: Unicode and strings, comprehensions, generators and iterators, unit testing and special method names are explained here as well as anywhere. Read it alongside a current source rather than instead of one, and skip the chapters marked above. Browse more Computer Science books for the rest of your semester.

Dive Into Python 3 by Mark Pilgrim. Copyright 2001–2011 Mark Pilgrim. Free under the Creative Commons Attribution-ShareAlike 3.0 licence. Official source: https://diveintopython3.net/

Leave a Comment