Errata

Learning Python

Errata for Learning Python, Sixth Edition

Submit your own errata for this product.

The errata list is a list of errors and their corrections that were found after the product was released. If the error was corrected in a later version or reprint the date of the correction will be displayed in the column titled "Date Corrected".

The following errata were submitted by our customers and approved as valid errors by the author or editor.

Color key: Serious technical mistake Minor technical mistake Language or formatting error Typo Question Note Update

Version Location Description Submitted By Date submitted Date corrected
Page DELEGATING BUILT-INS—OR NOT
Last paragraph

In chapter 28, section "DELEGATING BUILT-INS—OR NOT", last paragraph.
The sentence "The was a mod in Python 3.X, but isn’t a showstopper: delegation-based classes can still redefine operator-overloading methods to delegate them to wrapped objects, either manually or via tools or superclasses. "
It is perhaps a typo in the beginning "The was...". Not sure it shall be "There was".

Note from the Author or Editor:
Thanks for your post; we fixed this typo before the book was released.

Wenjie Wang  Nov 29, 2024  Feb 25, 2025
Page Listing instance attributes with __dict__
Example 31-11.testmixin.py

In the code of
def tester(listerclass, sept=False):
...
if sept: print(f'\n{'-' * 80}\n')

Above, 4 single quotes are used and will cause error.
It shall use a pair of double quotes contains a pair of single quotes, e.g.,
if sept: print(f"\n{'_' *80}\n")

Also, in the terminal output for
>>> from listinstance import ListInstance
....
>>> X = Hack('code')
>>> print(X) # print() and str() run __str__
<Instance of Hack, address 0x10c890b90:
data1='code'

There is a missing enclosing ">" right after "data1='code'


Note from the Author or Editor:
Thanks for your post. It raised two issues: a missing ">" in the output and f-string quotes. We added the missing ">" (in this example and elsewhere) prior to release.

The f-string issue is not an error: it uses Python 3.12+ f-string syntax documented earlier in Chapter 7 (page 164). In short, 3.12 and later allow nested quotes of the same type used for the enclosing f-string, which is a very useful feature demoed by the book intentionally. Because this syntax has already generated two errata posts, though, we added the comment "# Python 3.12+ f-string" to this line to call this out for readers using older Pythons.

Wenjie Wang  Dec 19, 2024  Feb 25, 2025
Page Example: A “Mix-in” Attribute Lister
Example 31-13. listtree.py

In the Example 31-13. listtree.py,
In the comments, the statement "-Supports classes with slots: lack of slots here ensures a __dict__". I figured the author meant to say "Supports classes without slots: ..." as classes with slots will not have __dict__.

Also in the definition of the method __attrnames
def __attrnames(self,obj, indent, unders=True):...
The "obj" argument actually plays the same role as "self". Is it redundant? Removing obj and replace it with self in the program yield the same output.

Finally, in the def __listclass(self, aClass, indent):...
there a for loop
for super in aClass.__bases__:...
"super" is a built-in function name, shall we in general avoid naming a variable with a built-in names?

Note from the Author or Editor:
Thanks for your post. It raises 3 issues:

1) No, the comment about slots is correct as is. Mixing in the ListTree class to any other will ensure that a __dict__ attribute is present in the instance. This is even true if it's mixed in to a class with __slots__: __slots__ normally precludes a __dict__, but ListTree (or any of the other lister classes) will ensure that one is added to the mix. See the coverage of slots ahead in this chapter and in Chapter 32.

2) No, the __attrnames() method is correct as is. Notice that this method is called in two places: to get the instance's names, and to get a class's names. "self" may seem redundant in the former, but "obj" and "self" differ in the latter, with "obj" being the subject class. This class will not work with the mod you suggested; please review its code again.

3) Yes, not a bug but potentially confusing, so we changed all "super" to "Super" in this example prior to release so they are distinct from the built-in.

Wenjie Wang  Dec 20, 2024  Feb 25, 2025
Page Classes are Types are Classes
Code after 1st paragraph

In the chapter 32 "Class Odds and Ends",
in the code right below the section title "section "The Python Object Model" , class "C" is not defined.
"
>>> class Hack: pass # A humble user-defined class
...
>>> I.__class__, C.__class__
(<class '__main__.Hack'>, <class 'type'>)
"
I believe the Author meant to use "Hack.__class__" rather than "C.__class__".

Note from the Author or Editor:
Thanks for your post. We fixed this to use the correct class name in your post before the book was released.

Anonymous  Dec 22, 2024  Feb 25, 2025
Page Chapter 37: "Unicode and Byte Strings", Section: "The Unicode Twilight Zone", Sub-section: "Unicode
Last code block

The last command in this code block references "M" twice. I believe the first one should be an "L" like in the previous command.

Original:
normalize('NFD', M) == normalize('NFD', M)

Suggested:
normalize('NFD', L) == normalize('NFD', M)

Note from the Author or Editor:
Thanks for your post. We fixed this to use the correct "L" name in your post before the book was released.

Tolga Yilmaz  Dec 22, 2024  Feb 25, 2025
Page And One “object” to Rule Them All
2nd paragraph

In chapter 32 "Class Odds and Ends" section "And one object rule them all" 2nd paragraph, there is a statement "to avoid combustion, keep in mind that isinstance is true for either a subclass relationship or creation source, though the latter may also imply inheritance through the secondary type-class tree for classes."

It is confusing that function isinstance will return True for a subclass relationship.
For instance, if class A is a subclass of class B, isinstance(A, B) will return False rather than True.
i.e.,
>>>class B: pass
>>>class A(B): pass
>>>isinstance(A, B)
False

Note from the Author or Editor:
Thanks for your post. We tightened this up before the book was released to make it clearer that the isinstance test is based on inheritance through the secondary type-class tree for classes. The primary class tree in your example doesn't apply in this case.

Anonymous  Dec 22, 2024  Feb 25, 2025
Page Inserting Code to Run on Attribute Access
3rd paragraph

On 6th edition, online early release version.
Chapter 38, Managed Attributes. Why Manage Attributes,
under section "Inserting Code to Run on Attribute Access".
Paragraph 3rd, 4 th bullet "The __get__ and __set__ descriptor methods, for handling access to a specific method and the basis for other tools such as properties and slots
"
Is "...handling access to a specific method..." supposed to be "... handling access to a specific attribute..."?

Note from the Author or Editor:
Thank for your post; for consistency, we changed this as suggested before the book was released.

Anonymous  Jan 13, 2025  Feb 25, 2025
Page Intercepting Built-in Operation Attributes
3rd paragraph

6th Edition, O'Reilly online early release version.
Chapter 38, section "Intercepting Built-in Operation Attributes", 3rd paragraph.
In the sentence "For example, attribute fetches for the __str__, __add__, and __getitem__ methods un implicitly by printing,..." there is a word "un" between words "methods" and "implicitly" that may be a typo.

Note from the Author or Editor:
Thank for your post; we fixed this typo to use "run" before the book was released.

Anonymous  Jan 16, 2025  Feb 25, 2025
Page Chapter 9, Built-in Type Gotchas
in section

Book states...

>>> D1 = {'b':3, 'a':1}
>>> D2 = {'a':1, 'b':3}
>>> D1 == D2
False
--

REPL for Python 3.3.1 returns True
>>> import sys
>>> print(sys.version)
3.13.1 (main, Dec 3 2024, 17:59:52) [Clang 16.0.0 (clang-1600.0.26.4)]
>>> D1 = {'b':3, 'a':1}
>>> D2 = {'a':1, 'b':3}
>>> D1 == D2
True

Note from the Author or Editor:
Thank for your post; we fixed this example to correctly show its output as True before the book was released.

Paul Stivers  Jan 29, 2025  Feb 25, 2025
Page Example 39-17. access1.py
def traceMe(*args)

In the code:
if traceMe: print(f'[{' '.join(map(str, args))}]')

In the print statement, two pairs of single quotes are used instead of nested double and single quotes. It causes error.


Note from the Author or Editor:
This is not an error: it uses Python 3.12+ f-string syntax documented earlier in Chapter 7 (page 164). In short, 3.12 and later allow nested quotes of the same type used for the enclosing f-string, which is a very useful feature demoed by the book intentionally. Because this syntax has already generated two errata posts, though, we added the comment "# Python 3.12+ f-string" to this line to call this out for readers using older Pythons.

Anonymous  Jan 30, 2025  Feb 25, 2025
Page Example 39-22. access_builtins_mixin_desc.py
The paragraph after Example 39-22.

The paragraph in Chapter 39 right after Code Example 39-22.
In the sentence "Hence, the loop at the end of this mic-in class is equivalent to the following statements, run in the mix-in class’s local scope:"
The word "mic-in" shall be "mix-in".

Note from the Author or Editor:
Thanks; we fixed this typo before the book was released.

Anonymous  Feb 02, 2025  Feb 25, 2025
Page Chapter 39, section "Open Issues"
subsection "Arbitrary arguments"

Chapter 39, Section of "Open Issues", subsection "Arbitrary arguments".
In the code snippet after the 3rd paragraph.
In the code snippet,
>>>def func(*kargs, **pargs): pass
>>> code = func.__code__
>>> code.co_nlocals, code.co_varnames

I believe the author meant to use "*pargs" and "**kargs" to represent the positional and keyword arguments, to be consistent with the context.

Same happened in the next code snippet.
>>>def func(a, b, *kargs, **pargs): pass

Note from the Author or Editor:
Thanks; we fixed this before the book was released. The code ran as shown, but swapped names "pargs" and "kargs" confusingly.

Wenjie Wang  Feb 04, 2025  Feb 25, 2025
Page Example 40-11. metainstance.py
def meth3(self)

In the Example 40-11. metainstance.py,
the indent for the block of the return in the "def meth3(self):" is one space less.

Note from the Author or Editor:
Thanks for your post; we fixed this before the book's release (not a bug, but inconsistent). We also changed a stray "MetaOne" in this example's comments to "Meta".

Anonymous  Feb 12, 2025  Feb 25, 2025
Page The Module Search Path
2nd paragraph

The author claims that "Special case: built-in modules like sys, coded in C and statically linked into Python, are always checked first before scanning the module search path."

I am running Python 3.11.
I am wondering if the special cases also include module like "os" that is not on the list of sys.builtin_module_names. os.__file__ indicates it is located in a Python folder listed as the 3rd directory in the sys.path. However, even if I make my own os.py in the current directory, the "import os" statement will still import the builtin os (repr(os) gives "<module 'os' (frozen)>"), i.e., it will not be masked by the user defined one in the current directory.

Note from the Author or Editor:
You're right: "os" cannot be redefined today even though it is not in sys.builtin_module_names. This is because it inhabits an additional and poorly documented "frozen" module set that, along with true built-ins, also takes precedence over sys.path search.

The book's coverage of the module search path focuses on what most programmers need to know and follows that in Python's own docs here:
https://docs.python.org/3/tutorial/modules.html#the-module-search-path

But "os" belongs to an additional category known as "frozens" that is similarly searched first as a special case. This category was added recently in Python 3.11 as a startup optimization, and is part of the version-specific and frequently morphing tale of module imports in 3.X. Given that the need to override something like "os" is very rare, most Python users don't need to care.

To reduce confusion, though, before release we added a brief mention of the frozen category and its "os" member to the book's module-search description, alongside that of true built-ins in the opening of this section. This section now begins:

"""
Special case: built-in modules like sys, coded in C and statically linked into Python, as well as frozen modules like os, optimized for faster startup as of Python 3.11, are always checked first before scanning the module search path and hence take precedence.
"""

Those who do need to care can find the rest of this story in Python's change logs and docs at python.org, which arguably could use some polishing. Some people were also burned by the "frozen" optimization in broken debuggers, so this 3.11 mod clearly merits a callout.

Anonymous  Feb 20, 2025  Feb 25, 2025
Page Chapter 23 Module Coding Basics
Subsection entitled "Imports are Runtime Assignments"

In the abstract example code:
if sometest:
from moduleA import name
else
from moduleB import name

There is a missing colon after "else".

Also, the earlier section title "Creation Modules" shall be "Creating Modules", to be consistent with the section titles "Using Modules" and "Reloading Modules".

Note from the Author or Editor:
Thanks for your post, and great finds. In this case, we caught both of these typos before your post came in and corrected them before the book's official release.

Anonymous  Feb 23, 2025  Feb 25, 2025
Page Chapter 24 Module packages
section "Package __init__.py Files" and "Package __main__.py files".

On both package structures for "Package __init__.py Files" and "Package __main__.py Files", there are 'mod__.py" under "dir1" folder, below "__init__.py". Not sure why there are addition underscores trailing the "mod", not consistent in this chapter that there two same-name modules "mod.py" in "dir1" and "dir1/dir2".

Note from the Author or Editor:
A typo in a folder-tree sketch, probably caused by copy/paste. To fix, on both pages 597 (last code block, line 4) and 599 (first code block, line 5), please change "mod__.py" to "mod.py".

Anonymous  Feb 27, 2025  May 09, 2025
ePub, Mobi, O'Reilly learning platform
Page Preface, page xxxvi
Sentence 2 of notebox that begins "The icon may..."

This sentence refers to the crow icon, which appears in the print and PDF version but is absent in the ePub, online, and Kindle versions. For accuracy, please prefix this sentence with: "For print and PDF readers:".

Mark Lutz
 
Feb 27, 2025  May 09, 2025
Page Example: Unit Tests with __name__
3rd and 4th paragraph

In the sentence "When we run this file as a top-level script, its name is set to __main__, so its self-test code kicks in automatically:..."

and "If we import the file, though, its name is not __main__, so we must explicitly call the function to make it run:" ,

There are missing quotation marks enclosing __main__.

__main__ itself is a builtin module. It is important to distinguish '__main__' and __main__.

Note from the Author or Editor:
This is in the second-last paragraph on page 624. Please change "When we run this file as a top-level script, its name is set to __main__," to "When we run this file as a top-level script, its name is set to the string __main__,".

Discussion: this should be readily apparent given the ample descriptions and code surrounding this text, but we'll insert "the string" for extra fidelity since it confused this poster. Adding quotes around the string here as suggested doesn't make sense; the quote-less form is used elsewhere too. And for the record, __main__ is not a builtin module (run a dir(builtins) to verify yourself), but that probably wasn't the cause of the confusion here.

Anonymous  Mar 04, 2025  May 09, 2025
Page section "Combined try Clauses"
1st paragraph

"we could either use a finally to ensure that cleanup code was always run, or write except blocks to catch and recover from specific exceptions and optionally specify an else clause for when exceptions occurred."
Here, I am wondering if the author meant to say "optionally specify else clause for when NO exceptions occurred."

Note from the Author or Editor:
Yes, a valid typo. This is at the end of paragraph 5 on page 894 in PDF and print. Please add a "no" there so this reads: "optionally specify an else clause
for when no exceptions occurred".

Anonymous  Mar 13, 2025  May 09, 2025
Page Exception Groups: Yet Another Star!
10th paragraph

10th paragraph, "And a basic except can catch a group as a collective and process it manually, but an except* cannot catch a group because it would be ambiguous (a schism of the sort that’s usually a hallmark of an ad hoc extension):"

Not sure why except* cannot catch a group,
the code below this paragraph:
"try:
... raise ExceptionGroup('Lots', [IndexError(), SyntaxError()])
except Exception as E:
...
"
yield the the exact same results if changing "except" to "except*".

Note from the Author or Editor:
This is at the top of page 925 in PDF and print. Please change the third line of the first code block on this page from the first of the following to the second:

except Exception as E:
except ExceptionGroup as E:

Discussion: the poster is right that except and except* work the same if Exception is used, but this is referring to catching a group explicitly--something that works in except but is a runtime error with except*. See PEP 654's "Forbidden Combinations" for more details; it's one of a handful of things described there that make the two exception models incompatible. Regrettably, the example demoing this wasn't as tight as it might have been. The reprints mod above should help clarify.

Anonymous  Mar 18, 2025  May 09, 2025
Page Generators are single-pass iterables
1st paragraph

In the subsection entitled "Generators are single-pass iterables" in chapter 20.
The first paragraph "... both generator functions and generator expressions are their own iterators and thus support just one active iteration". It may be more appropriate to say "... the generator objects created by both generator functions and generator expressions are their own iterators and thus support just one active iteration."

Note from the Author or Editor:
This is in line 1 of of the second-last paragraph on page 501 (in print and PDF). Please change "both generator functions and generator expressions" to "the objects returned by both generator functions and generator expressions". This seems splitting hairs over a small shade of meaning that should be obvious to most readers at this point in the book, but it's worth the edit to tighten this up, and there is ample room in this paragraph.

Anonymous  Apr 12, 2025  May 09, 2025
Page Generating “infinite” (well, indefinite) results
1st paragraph

In the sentence "This may sound more impressive that it is;", I guess "that" shall change to "than".

Note from the Author or Editor:
Yes, a trivial typo missed by many reads and readers during the book's development. Please correct as suggested in reprints, changing "that" to "than". This is in the last paragraph on page 505 in print and PDF.

Anonymous  Apr 12, 2025  May 09, 2025
Printed, PDF
Page Page 100 (in print and PDF)
First line at top of page

Minor typo: please change "float-point" to "floating-point" on this page. The latter is used everywhere else in the book, but the oddball here was missed in proofing.

Mark Lutz
 
Apr 14, 2025  May 09, 2025
Printed
Page Appendix A, pages 1050 and 1055
Figures A-8, A-9, A-13, A-14.

In the May 2025 reprint, we're replacing a few dark-mode screenshots in Appendix A with light-mode equivalents because the dark versions sometimes caused minor smudging or creasing in the print book. The content of the new shots is functionally the same; they're simply lighter to avoid printing issues.

Mark Lutz
 
Apr 29, 2025  May 09, 2025
Page Chapter 31, Section "How the MRO Works"
1st paragraph

Algorithm proposed by the author is quite different from the C3 algorithm, which Python actually uses and is described in detail in the "The Python 2.3 Method Resolution Order" article by Michele Simionato found in the appendix to the official documentation. While the authors version works for the simple examples that follow in the book, it is incorrect. As a counterexample please consider following class hierarchy from the mentioned article:

class D: pass
class E: pass
class F: pass
class B(D, E): pass
class C(D, F): pass
class A(B, C): pass

When you apply the author's algorithm, you get:

DFLR => [A, B, D, object, E, object, C, D, object, F, object]
MRO => [A, B, E, C, D, F, object]

This puts E before C and D, contrary to the C3 algorithm answer given by the interpreter.

>>> [c.__name__ for c in A.mro()]
['A', 'B', 'C', 'D', 'E', 'F', 'object']

Note from the Author or Editor:
Please add the following text for other readers like this poster who may wrongly assume that the book's coverage of the MRO algorithm is exhaustive:

"""
Fine print: There's more to the MRO algorithm than this book can cover. Its brief sketch here is an incomplete approximation by design and won't yield accurate results for some complex class trees. You can find all the gory MRO details in Python's docs, but keeping your class trees simple will also keep them immune from the more convoluted bits of this wildly esoteric algorithm. Whether in your code or Python itself, obfuscation is rarely good engineering.
"""

Let's try adding it first as a new note box after the last sentence on page 800, with its "Fine print" in italics.  This will change page breaks here, but there's some space on page 802 for spillover.  Please run past me; this may also work as a new footnote on page 800 with the footnote marker at the end of the last sentence on this page, if that changes paging less intrusively.

[Discussion]  This reader notes that the book's terse coverage of the MRO algorithm in Chapter 31 was incomplete and did not yield correct results in some cases more advanced than those shown in the book.

While technically true, this is by design, and criticism of it misses the point of this book.  Learning Python is a resource for newcomers.  It's not an exhaustive reference and not a PHD thesis meant to demo author intelligence.  Much like this book's limited material on type hinting and coroutines, its MRO coverage is meant to introduce this wildly complex topic, not to cover it in full.

Hence, this book's MRO sketch is intentionally a first-order approximation, and readers can easily find complete details online if and when needed.  But to be blunt, the full MRO algorithm, C3, is nauseatingly convoluted.  Its esoteric and artificial complexities have no place in a book like Learning Python.  They probably should not have found purchase in Python either, but this is now a historical choice point about which intelligent people can disagree.

To this reader: thanks for your input, kudos for digging further on your own as the book intended, and you are welcome to document the full MRO algorithm in a Python book of your own.  But if you do, you probably shouldn't expect readers to be enthusiastic about digging deeply into a complex tool that obfuscates their code.  Learning Python, like Python itself, is not supposed to be just for "smart" people.

Michał Jakubowski  Jun 12, 2025  Aug 08, 2025
Page page 194
third paragraph from the bottom

The text says: "\t in a Python string means vertical tab". \t means horizontal tab.

Note from the Author or Editor:
Yes, a minor yet obvious typo; please change "vertical" to "horizontal" here. We get this right numerous places elsewhere in the book (including the formal def in Table 7-2 on p133) but the text here is clearly askew.

Aleksey Tsalolikhin  Sep 16, 2025  Dec 12, 2025
Page page 785
use output file instead of a stream example

System: Windows 11 Home version 24H2
Python version 3.14.0

book example:
$ python3
>>> import converters
>>> scan = converters.Uppercase(open('trihack.txt'), open('trihackup.txt', 'w'))
>>> scan.process()

At my system this will only create and open the new trihackup.txt file. There is nothing writen to it. If the code '.process()' in the last line is placed behind the code in the line above it the code runs fine:

$ python3
>>> import converters
>>> scan = converters.Uppercase(open('trihack.txt'), open('trihackup.txt', 'w')).process()
>>> scan

Note from the Author or Editor:
[Edits: not a bug, but to avoid similar confusion, please expand the sentence on page 785 paragraph 2 from the first of the following to the second, which shouldn't add a line break:
"Here, we use an output file instead of a stream:"
"Here, we use an output file instead of a stream (be sure to exit your REPL to finalize the file):"]

This example works as shown in the book, but your post raises an interesting point. Your mod isn't obviously different: it simply runs process() on the Uppercase object immediately instead of on the next line, and the Uppercase object retains references to the passed in file objects either way (via the Processor superclass's reader/writer attributes).

Subtly, though, the embedded file objects won't be garbage collected—and hence automatically flushed and closed—until there are no more references to them. The book's version retains a reference in the "scan" variable, but your mod does not: your "scan" is None, the result of calling process(). Hence, your version triggers file flush and close just by losing references to the files, and this finalizes file content. Some REPLs might hold onto file objects even longer, subverting both versions and differing from normal script execution.

All of this is covered by the book on page 215, and mentioned again on pages 223, 908, 973, and 776 (search for "auto-close" in e-media). In this particular example, the book echoes the file in a system shell, which implies that the Python REPL was exited. However, if you suspend the REPL instead of closing it (e.g., with ctrl-Z on Unix) or inspect the file in another window, the output file may not yet be complete.

As suggested in the book, if you really want to avoid incomplete files like this before REPL exit, you must explicitly close the files with code like this:

$ python3
>>> import converters
>>> infile = open('trihack.txt')
>>> outfile = open('trihackup.txt', 'w')
>>> scan = converters.Uppercase(infile, outfile)
>>> scan.process()
>>> infile.close()
>>> outfile.close()
>>> (ctrl-Z)
[1]+ Stopped

$ cat trihackup.txt
HACK
HACK
HACK!

As you can see, this requires substantially more code: 7 lines versus 3, sans compression coding tricks. Which is why it's not always spelled out this way in the book for simpler REPL examples like this one.

Anonymous  Oct 12, 2025  Dec 12, 2025
Page Page 450 (Chapter 18 Arguments - Using Keyword-Only Arguments)
Last line of the last paragraph

"A similar case can be made for positional-inly arguments versus manual code"

I suppose that "positional-inly" here should be "positional-only".

Note from the Author or Editor:
Thanks for the post, and good catch. Edits: please change "inly" to "only" as suggested.

Wu Qi  Dec 07, 2025  Dec 12, 2025
Page 22
Last sentence under 'CPython: the standard'

'though this it prone to change' looks like it was supposed to be spelled 'though this is prone to change'

Note from the Author or Editor:
Yes - a minor typo, and thanks for reporting it.

Anonymous  Sep 22, 2025  Dec 12, 2025
Page 62
Third example ending in "# Ditto, with nested quotes"

The nested quotes example as written has nested single quotes which results in the following error (note the ^^^^ in the third line of the error should line up under sapp):

>>> f'{296999.256:,.2f} | {'sapp'[1:]}'

File "<stdin>", line 1
f'{296999.256:,.2f} | {'sapp'[1:]}'
^^^^
SyntaxError: f-string: expecting '}'

I was expecting this sort of error when I recognized the nested single quotes. This error also occurs if all the quotes are double. However, changing the nested quote example to either of the following results in the correct solution:

>>> f'{296999.256:,.2f} | {"sapp"[1:]}'
'296,999.26 | app'

>>> f"{296999.256:,.2f} | {'sapp'[1:]}"
'296,999.26 | app'

I doubt it matters, but I am using Python 3.10.12

Note from the Author or Editor:
Please change the comment on the right of this line to the following, adding the "Python 3.12+" part and retaining its current indentation (if this exceeds line-width limits, drop the "+" or let me know):

# Ditto, with Python 3.12+ nested quotes

[Discussion] No, this example works as shown by the book in Python 3.12 or later. As described in full on page 164 ahead, Python 3.12 relaxed f-string syntax to allow nested quotes of the same kind as those used to enclose the string. Because the book uses Python 3.12, this is fair game in its examples:

~$ python3.12 -q
>>> f'{296999.256:,.2f} | {'sapp'[1:]}'
'296,999.26 | app'

That said, you are the third errata-list poster to be burned by this, and the nested quotes coverage doesn't appear till later. So, we'll augment this example's comment and retain this note for others who may run into the same pitfall. But when in doubt, try examples on 3.12+; mixing quotes works but is no longer required.

Anonymous  Sep 25, 2025  Dec 12, 2025
Printed, PDF, ePub, Mobi, O'Reilly learning platform, Other Digital Version
Page 168
Bottom of page

Please add a new footnote at the bottom of page 168, with a footnote reference at the very end of the sidebar on this page, and text that reads as follows, with its t'...' part in literal/code font and its "yet another" in italics.
If this changes page breaks, it can be shortened:

"""
Postscript: after this book was published, Python 3.14 added yet another string-formatting tool known as template strings and coded as t'...'. These are happily out of scope here; see Python's docs for more on its ever-expanding formatting tale.
"""

[Discussion]. As if three (really four) formatting tools wasn't enough?!

Mark Lutz
 
Jul 29, 2025  Aug 08, 2025
Page 212
Last code snippet

Unbalanced parentheses in output.
An extra left parenthesis appears at the beginning of the printed tuple.

# Incorrect Output:
(('Pat', ['dev', 'mgr'])

# Correct Output:
('Pat', ['dev', 'mgr'])

Note from the Author or Editor:
Please delete the opening "(" on this code line, per the post.

[Discussion] Another admirable catch of a typo missed during book development. This code was obviously run for verification, but the extra parenthesis probably stems from a bad cut-and-paste. It's minor, given the enormous number of code lines pasted correctly, but certainly worth the patch.

Rickard K  Jun 10, 2025  Aug 08, 2025
Page 217
Last code snippet

# Description:
The file path string uses a typographic (curly) opening single quote (‘) instead of a standard straight single quote ('), which results in a syntax error in Python.

# Incorrect:
open(‘C:\\Users\\me\\code\\newdata.txt')

# Correct:
open('C:\\Users\\me\\code\\newdata.txt')

Note from the Author or Editor:
Please make the slanted quote straight near the start of this code line, per this post.

[Discussion] This example is not run in the book, but yes, another typo that was present in the initial submit and missed by many proofing cycles. It happens, but another good catch (and great eyesight).

Rickard K  Jun 10, 2025  Aug 08, 2025
Page 218
Code snippet, 2nd line

A period appears at the end of the statement.

# Incorrect:
myfile.write(b'\x00\x01hack\x02\x03').

# Correct:
myfile.write(b'\x00\x01hack\x02\x03')

Note from the Author or Editor:
Please delete the period in the code line per the post, taking care to retain the alignment of the "#" comment to the right with those surrounding it.

[Discussion] A valid typo, also present in the initial material, missed by many proofreads, and almost certainly caused by an automatic insert in Word. Good catch!

Rickard K  Jun 11, 2025  Aug 08, 2025
Page 279
Last bullet point

The last bullet point uses the term "keyword", even though the author explicitly states on a previous page that he will use the term "reserved word" throughout the book.

Note from the Author or Editor:
Please change "soft keyword" to "soft reserved word" in the last bullet on page 279, per this post.

[Discussion] This seems trivial, but agreed that the pages preceding this use "reserved word" instead of "keyword" and this should too. In fact, there was a deliberate effort in the book to distinguish between this use of "keyword" and its very different meaning in function arguments (something Python docs have historically struggled with). This appearance seems to have evaded the book's best efforts to keep the usages distinct.

Rickard K  Jun 20, 2025  Aug 08, 2025
Page 279
4th paragraph under the "Naming conventions" section

The variable name a_Long_name uses mixed casing and underscores.

The variable names should use lowercase letters with words separated by underscores (snake_case).

# Incorrect:
a_Long_name

# Correct:
a_long_name

Note from the Author or Editor:
Please change "a_Long_name" to "a_long_name" in paragraph -3 of page 279.

[Discussion] No, this is not a mistake in Python, it's the poster's stylistic preference and seems to be picking nits too much. There is nothing at all wrong with using "a_Long_name" for a variable, either syntactically or stylistically. In fact, it's arguably useful to show that this works as is.

That said, since this was called out, it feels slightly better to go with either all lower or upper case here just to make the name jive with the "aLongName" camel-case version preceding it. But again, it's not a mistake, and please don't post opinions as such; category changed.

Rickard K  Jun 20, 2025  Aug 08, 2025
Page 298
Example 12-1. matchdemo.py

I am using Python 3.13.2 on Win10.
The input state = (1, 2, 3) is being interpreted as a list, not a tuple.

Note from the Author or Editor:
[Edits: not an errata, changed from "Serious technical mistake" to "Question" and retained for other readers. But to minimize future confusion, please change the last clause on page 297 from the first of the following to the second, which shouldn't add a page break:
"""(pasters beware: blank lines added for readability here won’t work in a REPL, and […] patterns preclude (…)s; run from a file and experiment freely)."""
"""(coding footnotes: blank lines added to this example for readability won’t work if pasted in a REPL, and its earlier […] patterns preclude its later (…) patterns; run from a file, and mod the code for more insights)."""]

The book is right about this. This is the expected, if confusing, behavior of "match" statement patterns, and is mentioned in the book (albeit, tersely). As stated just before this example:

"""
As an artificial (if frightening) example, Example 12-1 demos literal, sequence, and mapping patterns. Its [...] and (...) patterns both match any sequence and are interchangeable; <more text trimmed>, and [...] patterns preclude (...)s; run from a file and experiment freely).
"""

In other words, a [...] pattern will intercept a tuple sequence before it reaches a (...) pattern, and a (...) will intercept a list before it reaches a [...]. Hence, to make the tuple clauses match in the example, you must delete or comment out the list clauses so they don't intercept the sequence first (ignoring indentation lost here):

"""
match state:

#case [1, 2, what]: # Match sequence (1), what = 3
# print('list', what
#case [0, *what]: # Match sequence (0), what = [2, 3]
# print('list', what)

case (1, 2, what): # Match sequence: same as [1, 2, what]
print('tuple', what)
"""

That's why this section suggests experimenting on your own and passes on further elaboration; the "match" statement's semantics are both unique and highly convoluted (and perhaps needlessly so).

Admin note: in the future, please explore minor code mods before assuming a serious technical mistake; errata happen, but they tend to be the exception.

Cevdet NACAR  Nov 05, 2025  Dec 12, 2025
Page 431-432
Last section p. 431 and first section p. 432

The function call is invalid. Positional arguments cannot appear after keyword arguments.

func(title='Learning Python', 6, 2024, 3.12)
SyntaxError: positional argument follows keyword argument

Replace with
func('Learning Python', 6, 2024, 3.12) and update the text to say four naked values instead of three.

Note from the Author or Editor:
Please change the code line and text per this post's suggestions (deleting the code's "title=" and changing "three" to "four" in the text preceding it). This is at the bottom/top of page 431/432 in PDF and print.

[Discussion] Yes, good catch and you're absolutely right--keyword arguments cannot precede positional arguments, as covered in great detail in this book (including the immediately preceding page!). This example is not run in the book and was apparently an abstract example edited carelessly. It's more typo than "serious technical mistake" given the very many correct descriptions and demos of argument ordering in the book (and its category has been updated accordingly), but it merits a patch.

Rickard K  Jul 13, 2025  Aug 08, 2025
Printed, PDF, ePub, Mobi, O'Reilly learning platform
Page 788
Middle of paragraph 4

Please change [All attributes are all “public” and “virtual,”] to [All attributes are “public” and “virtual,”], deleting the redundant "all". A very minor typo in a very big book but worth patching.

Mark Lutz
 
May 04, 2025  May 09, 2025
PDF, ePub, Mobi, O'Reilly learning platform, Other Digital Version
Page 993 (Ch 38's into)
Sentence 2 of paragraph 1

In both emedia and the online chapter 38, please swap "last" and "first" in the sentence:

"For example, the last two techniques listed here apply to specific attributes, whereas the first two are generic enough..."

So that it reads:

"For example, the first two techniques listed here apply to specific attributes, whereas the last two are generic enough..."

[Discussion]. This does not impact the print book, because this chapter was moved online, but it affects all emedia (e.g., PDF) and should be pushed out to the online chapter too. It reflects a last-minute (QC1) reordering of the list preceding this sentence to reflect the chapter's presentation order. This sentence wasn't updated to match, though the extensive coverage surrounding it should make its gaff obvious.

Mark Lutz
 
May 12, 2025  Aug 08, 2025
Printed, PDF
Page 1153 (in print and PDF)
First sentence in last pragraph on page

The name of the free Python app that demos PC/phone portability appears later in Figure A-17, but should also be spelled out in the narrative. To do so, please change "standalone apps for Android, running on" on this page to "standalone apps for Android, PC-Phone USB Sync, running on", with its "PC-Phone USB Sync" insert in italics. There is ample space for the insert.

Mark Lutz
 
Apr 14, 2025  May 09, 2025
Printed, PDF, ePub, Mobi, O'Reilly learning platform
Page 1155, Appendix A
End of bullet list

For completeness, please add a new bullet at the end of this list of other ways to run Python code, which reads as follows, with its "Sandboxes" in italics:

"""
Web interfaces like the O'Reilly platform's Sandboxes
"""

[Discussion] This book isn't a marketing tool, but the sandboxes require no installs and work well for those who are able to buy or use a subscription to the platform. It's also largely just Linux for some users, so all of that platform's info applies. There is no mention in Chapter 3, but this book can't be comprehensive about perpetually changing topics.

Mark Lutz
 
Jul 29, 2025  Aug 08, 2025