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.
| 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 |