Errata

Learning Python

Errata for Learning Python

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
Printed, PDF, ePub, Mobi, , Other Digital Version
Page N/A
N/A

[Informational only: no changes required.] A new first beta version of PyPy with support for Python 3.2.3 was announced on August 1, 2013. If successful, PyPy will no longer be a Python 2.X-only system.

This edition discusses the PyPy optimized Python implementation repeatedly -- in the Preface, in Chapter 2's implementations overview, and in Chapter 21's benchmarking study. When this material was written, PyPy was accurately described as 2.X-only, with caveats about possible future evolution; half a year later, PyPy has acquired both ARM and Python 3.X support. See pypy.org for up-to-date details.

Mark Lutz
 
Aug 02, 2013 
Printed, PDF, ePub, Mobi,
Page N/A
N/A

[Supplemental resources] At the following pages I've started collecting reader FAQs and Python change notes which are too large to post on this errata list, and do not require changes in book reprints:

--Reader queries: learning-python.com/book-queries-lp5e.html
--Python changes: learning-python.com/python-changes-2014-plus.html

Per the Preface, you'll find these and other links at the book's support page at learning-python.com/about-lp5e.html. (This site moved from its prior www.rmi.net/~lutz location in October, 2015.)

Mark Lutz
 
Oct 08, 2013 
Printed
Page xlvi
Fetching... section

The website for fetching code is listed incorrectly as http://oreil.ly/LearningPython

That url is also given on page xlvii as the publisher's site.

It didn't take long to find the correct url and took me to some strange places.

Great book, I'm enjoying and learning a lot so far. And I like the detail and forward references. Roberto

Note from the Author or Editor:
Did you miss the "-5E" at the end of the URL? It appears as the following correct/working URL in both the paper version and ebooks (PDF, Mobi, and Epub versions are all correct as far as I can tell):

http://oreil.ly/LearningPython-5E

If there is another format/viewer where the URL is not appearing correctly, please repost this issue with more details.

Roberto Riley  Nov 18, 2013 
Printed, PDF, ePub
Page Multiple
Solution links at end of each part's exercises

Reported by a reader offline: the hyperinks to exercise solutions at the end of each part are not very useful as is. They typically are coded as variations on:

"See Part _X_ in _Appendix D_ for solutions"

with a link from "X" to the start of the part itself, and a link from "Appendix D" to the start of the entire appendix. The first of these seems almost useless, as it points back to the start of the part just read. The second takes readers to the appendix start, but they then have to scroll to the portion within it having the part's answers.

I'm open to ideas (or O'Reilly standards) on this, but at least one of these two links should point to the subsection of Appendix D for the specific part's answers. As a model for how these links should ideally work, see the Index entry for "exercises", which has correct subsection links; for each part, the first link gives the page having the questions, and the second is the page with their solutions:
"""
exercises
part I, 87–89, 1465–1467
part II, 313–315, 1468–1472
part III, 467–469, 1473–1475
part IV, 663–665, 1475–1485
part V, 778–779, 1485–1489
part VI, 1072–1076, 1489–1496
part VII, 1161, 1497–1499
"""

Locations where these links appear along with their text (the wording itself is fine and probably shouldn't be changed; only the links need be adjusted):

Page 87: "Be sure to check Part I in Appendix D for the answers"
Part I links to the part start, Appendix D to appendix start.
We really need only Appendix D to link to page 1465.
Page 313: "answers in Appendix D"
Appendix D links to appendix start on page 1465, but should be
to this part's subsection start on page 1467/1468
Page 467: "See Part III in Appendix D for the solutions"
Same pattern as page 87: need only link to page 1473 instead.
Page 663: "solutions in Part IV in Appendix D,"
Ditto; need only link to page 1475 instead.
Page 778: "See Part V in Appendix D for the solutions."
Ditto; need only link to page 1485 instead.
Page 1072: "You’ll find them in Appendix D, under Part VI."
Ditto (order swapped): need only link to page 1489 instead
Page 1161: "See Part VII in Appendix D for the solutions."
Same: need only link to page 1497 instead.

If this is unclear please contact me prior to reprint edits.

Mark Lutz
 
Oct 18, 2014  Oct 29, 2014
Printed, PDF, ePub, Mobi,
Page various--see description
see description

URL CHANGE:
The book support site mentioned on various pages has moved from:
http://www.rmi.net/~lutz
to its new home at:
http://learning-python.com/books
The prior location will retain forwarding pages if you wind up there due to old printings or links, but please adjust any references to the old site.

EDIT: For reader convenience, let's change this in reprints. Specifically, run a global replacement of both of the following:
http://www.rmi.net/~lutz
http://home.rmi.net/~lutz
to the site's new URL:
http://learning-python.com/books

This includes appearances in the book's text; in two code listings (where there seems ample space for the change), and in any hyperlinks. I count the following pages as occurrences, but there may be a few more in links:
xxxiv
xlviii (twice!)
413 (in code, as home.rmi.net)
1416 (in code)
1541 (in "multiple About the Author" links)

While we're updating the bio, on page 1541, change "Spring 2013" to "Summer 2015", and "400,000" to "over 500,000" (with thanks to readers).

Mark Lutz
 
Nov 12, 2015  Nov 25, 2015
Printed
Page nowhere
nowhere

On page 217, the '%' operator is mentioned in the formatting context as also serving to do modulus arithmetic. That is the only mention in the book of that operator.. It is not in the symbols or alphabetic sections of the index nor in the description of divisive operators in pages 146 through 150.

Note from the Author or Editor:
This is just an omission in the index: "%" as numeric modulus does show up in multiple other places, especially in chapters 4 and 5, where it's both described and demonstrated. It's also used in later examples, and occasionally and informally called a remainder in other references (e.g., page 141 and Table 5-2). This operator is not present in the new-style division section, as it would muddy the waters there too much.

To improve the index for "%", please:

1) Change the page numbers for index entries "modulus operator" from "224" to "114, 141, 146, 224, 405, 407, 606"
2) Add the same page list (or a subset of it) as subtitle "numeric modulus operator" to the index entry for "% (percent sign)" (or simply note as "see also modulus operator")

These page numbers are all per the latest (Sep-2016+) printings. There are additional appearances in pages 648 and 687, but these seem too minor to index. I can live with shortening the additions if required for space; please let me know.

A general recommendation: I suggest searching one of the ebook formats of the book if the index fails to turn up anything. Indexing is regrettably done by third parties at the very end of the book production cycle under a tight schedule; it's gotten much better (one prior edition actually indexed the word "you"!), but it can still miss a few things.

Douglas Moffitt  Mar 10, 2017  Apr 21, 2017
PDF
spam_class.py

In chapter 32, the listing for spam_class.py, class Sub, method printNumInstances has a call to Spam.printNumInstances(); it should be Spam.printNumInstances(cls)

I am reading this on Safari so I don't have an actual page number. Also, the code on github is missing the same argument.

Note from the Author or Editor:
[Not errata, no changes required, informational reply only]

This example spans pages 1067-1069 in the latest printing (and 1031-1033 in older printings). It is correct as coded, run, and described in the book, but I'm retaining this post because it may help other readers understand the subtle nature of the example. First of all, keep in mind that the core idea of class methods is that they are always _automatically_ passed a class object first argument, whether called from instance or class. So, in the example's code:

x = Sub() # Makes an instance of subclass
x.printNumInstances() # Passes x.__class__ (i.e., Sub) to cls of Sub method
Spam.printNumInstances() # In Sub: passes Spam to cls of Spam method

You can see this in the first call's output shown, which traces the value of the cls argument. Notice how cls is first Sub in the subclass's method (line2), but then Spam in the manually-called superclass's method (line 3); both cls arguments were passed in automatically:

>>> x.printNumInstances()
Extra stuff... <class 'spam_class.Sub'>
Number of instances: 2 <class 'spam_class.Spam'>

The Sub.printNumInstances() direct call shown works the same as calling from instance x; Sub's method is invoked with Sub, which invokes Spam's method with Spam. The larger point of this example, though, is that the _lowest_ class of the call's subject is passed to class methods; in the following, Other is passed to the Spam method's cls, because there is no subclass redefinition to catch and reroute the call with a different automatic cls:

>>> z = Other()
>>> z.printNumInstances()
Number of instances: 3 <class 'spam_class.Other'>

That may better capture the utility of class methods--to process per-class data--than the Sub subclass example. For another example that further illustrates the effects of the automatic first argument in class methods, see http://www.learning-python.com/classmethods.py.

Joe Stewart  Oct 02, 2017  Oct 27, 2017
Other Digital Version
N/A
https://www.oreilly.com/catalog/errata.csp?isbn=0636920028154

The Reader FAQs hyperlink listed on the Confirmed Errata page returns a 404 error.

Note from the Author or Editor:
Indeed, this support page moved somewhere along the way (alas, engineers love to change things). It's new-and-improved rendition today lives at:

https://www.learning-python.com/book-queries-lp5e.html

No book changes are required. The original note on this page has also been updated for the new address (along with a few other dead links here), but the category was changed to generic Update as this isn't an issue with the book itself.

Chris Schnaars  Nov 13, 2019  Nov 22, 2019
Printed, PDF, ePub, Mobi,
Page Various
N/A

[No book changes required, informational only] A reader wrote by email to ask for help using bytecode files without source code in Python 3.X, a technique mentioned by the text as an option. This still works but has indeed changed: it today requires bytecode files to be renamed to discard the 3.2+ creator tag in their filenames. If you run into similar issues, check out the following post for full details and examples:

https://www.learning-python.com/book-queries-lp5e.html#q_7

Mark Lutz
 
Nov 17, 2019  Nov 22, 2019
Printed
Page v
(anywhere)

I'm impressed with the detailed explanation of how to use the yield statement. But "yield" actually returns a value, not necessarily None, which allows the generator to receive values from its caller as well as send values to it, thus implementing coroutines. A simple demo can be seen at www.panix.com/~wje/x20191120a.py.txt
This is huge, but your book is already huge. Probably not worthwhile mentioning that yield returns a value, right?

Note from the Author or Editor:
[No changes required] No, the value returned to send() by a yield expression is explicitly discussed later in the chapter; did you skip over this? Please see the coverage starting on page 618 for this book's exploration of this topic. There's even a question on this coverage in this errata list; see the page 619 reply.

Different people learn in different ways, of course. But for future reference, I recommend reading a section in its entirety before wandering the web for assistance (or posting unfounded suggestions to this page).

And if you want to roam the web anyhow, see also the two related examples on my books-support website; a simple search there might have turned these up:

https://learning-python.com/gendemo.py
https://learning-python.com/async1.py

Bill Evans  Nov 20, 2019 
Printed
Page v
(anywhere)

For better program portability (and other reasons), any chance you could document the pathlib module? It's an excellent alternative to os.path. (I know your books are already thick enough! [And quite useful, btw.])

Note from the Author or Editor:
[No changes required] Are you sure that you're posting to the right book page? LP5E does not cover system libraries like os.path at all. In fact, that library shows up just 7 times, and only in undocumented self-study code in the exercise solutions appendix. The pathlib module may or may not be a useful alternative to os.path, but it's radically off-topic and out-of-scope for LP5E. This is the domain of applications books like Programming Python.

Just as importantly, this book cannot cater to the personal preferences of every reader. I suggest touring the standard library's docs after you've mastered language basics; there's a lot of good stuff there, but it's a second step, and far too much for a language-focused text like LP5E to cover (especially when it seems to change every other month).

Bill Evans  Nov 20, 2019 
chapter 7
subsection 'Indexing and Slicing'

As from the book it is:
You can also use a negative stride to collect items in the opposite order. For example, the slicing expression "hello"[::−1] returns the new string "olleh"—the first two bounds default to 0 and the length of the sequence.

But it should be
In the case of negative stride the first two bounds will be( length of the string minus one) and (-1) .Though length of the string(instead of length of string minus one) will produce the same result but the 0 as default value of second bound will prevent the slicing of first character of string and it will not appear in reversed string.

Note from the Author or Editor:
REPRINTS: please change the referenced clause from:
"""
the first two bounds default to 0 and the length of the sequence, as before, and
"""
to this:
"""
the first two bounds effectively default to sequence length-1 and -1 (they really default to None and None, but that's unimportant here), and
"""
This is in paragraph 5 of page 210 (203 in earlier printings). It looks like this will fit without changing page breaks, but please verify.

DISCUSSION: this crops up in just one clause of one sentence of a very large text (and the rules may have changed over this title's lifetime), but the book's description here is too loose, even with the clarifying test and related examples that follow. In truth, though, neither the book nor this post's suggestion is quite right about defaults for slices with negative strides, and the full story is far too much to cover at this point in the text.

But in short: omitted bounds in this context are not treated the same as default values in others. When strides are negative and bounds are omitted, they invoke special semantics that are available only for omitted bounds, and are asymmetric with other slice usage. No explicit or default values work the same--except Nones.

This is an odd, special-case corner of Python which is of very little practical consequence, and doesn't merit full coverage either here or in the text. The briefest synopsis requires a look under the hood at the slice objects employed by slice expressions. As described in the book, slice expressions are the same as indexing with a slice object:

>>> S = 'hello'
>>> len(S)
5
>>> S[0: len(S): 1], S[slice(0, len(S), 1)]
('hello', 'hello')

Internally, a slice with just a -1 stride maps to the following; its (A, B, C) output is indices used to extract the slice as usual--from A, up to but not including B, by C:

>>> slice(None, None, -1).indices(len(S)) # this is [::-1]
(4, -1, -1)

But neither the post's proposal nor the book's handwaving work the same:

>>> slice(len(S)-1, -1, -1).indices(len(S)) # not same as [::-1]
(4, 4, -1)
>>> slice(0, len(S), -1).indices(len(S)) # not same as [::-1]
(0, 4, -1)

And no other integer defaults are treated the same as omissions:

>>> slice(len(S), -1, -1).indices(len(S)) # stop made positive=end
(4, 4, -1)
>>> slice(len(S)+1, 0, -1).indices(len(S)) # start scaled to len()-1 max
(4, 0, -1)

Among the asymmetry here: slice objects map an omitted stop of None to -1, but a literal -1 stop used in a slice expression is scaled to len()-1, the rightmost item. Hence, there is no way to code a numeric value that works the same as an omitted value, and the notion of integer defaults is invalid in this context:

>>> S[::-1] # right
'olleh'
>>> S[0:len(S):-1] # not right (book)
''
>>> S[len(S)-1:-1:-1] # also not right (post)
''
>>> S[len(S)+1:0:-1] # also also not right (hmm)
'olle'
>>> S[None:None:-1] # the only real equivalent
'olleh'

All of which confusingly differs from the case for a positive stride, where defaults 0 and sequence length are the same as both Nones and omission:

>>> slice(0, len(S), 1).indices(len(S))
(0, 5, 1)
>>> slice(None, None, 1).indices(len(S))
(0, 5, 1)

>>> S[::1]
'hello'
>>> S[0:len(S):1]
'hello'
>>> S[None:None:1]
'hello'

The upshot is that there is _no_ integer-default equivalent to omitted limits when negative strides are used (only!). The patch to the book's sentence hints as much, without devoting lots of space to an issue that's ultimately too obscure for prime time. In the book's defense, it does already show the following in the same section (and cover the slice() equivalence more in the classes part), which will match the patched clause well:

>>> S[slice(None, None, -1)] # how [::-1] works (despite the former clause)
'olleh'

If you wish to dig further, try both Python's manual for a (painfully cryptic) definition of the slice operation, and the PySlice_GetIndices() function in the C implementation of slice objects for the true mapping of bounds:

https://docs.python.org/3/library/stdtypes.html#common-sequence-operations
https://github.com/python/cpython/blob/master/Objects/sliceobject.c

Neerajbhatt Agastyamuni  Mar 15, 2020  Jun 05, 2020
Printed, PDF, ePub, Mobi, , Other Digital Version
Page n/a
(examples zipfile)

BOOK EXAMPLES: This book's examples package was rezipped without content changes in May 2020 to work around a bug in Finder-click unzips on Mac OS Catalina (in short, Catalina clicks fail with an "Error 22" on zipfiles that unzip correctly in other tools and on older Macs). The new package zip is available on my website, at:

https://learning-python.com/lp5e-code-1.0-may2020.zip

REPRINTS: please forward this on as needed, so that the book-page examples link at oreilly.com is updated to use the new rezipped version. There are more details about the Mac OS unzip failures in the package's top-level README.txt. The zipfile works fine on Mac OS after being repackaged with ziptools (at learning-python.com/ziptools.html).

Mark Lutz
 
May 21, 2020  Jun 05, 2020
Printed
Page n/a
(errata page)

A WORD ON ETIQUETTE: some recent posts here have regrettably warranted a clarification on the role of this page. In short: this page is not a blog's comments section. Please don't post here unless you've found a genuine book erratum, or have a legitimate book-related question.

I'm happy to receive tangible reports on book issues, and glad to pass along replies to honest queries that may be useful to other readers. But subjective content suggestions are not helpful in this context, especially when they are founded on something other than domain knowledge or careful research.

So please play nice; most posters do. If you're intent on broadcasting a personal opinion here anyhow, at least get your ducks in a row first.

Mark Lutz
 
May 23, 2020 
Printed, PDF, ePub, Mobi, , Other Digital Version
Page Assorted (see Description)
See Description

A recent chapter reread turned up a rare but patch-worthy hyphenation issue.

This book's policy is to use a hyphen in "command-line" only when the two words are used as a compound adjective (e.g., "command-line flag"). The hyphen should not appear in other contexts (e.g., "the command line"). We got this right in most of the book (which includes some 100 "command-line" and 200 "command line"), but five hyphens apparently slipped through the cracks, and one should be added.

[EDIT] Please replace the hyphen with a space in all five of these (also locatable via a text search in the PDF):

- Page 471, paragraph 4, line 2: "command-line like any of the"
- Page 670, code para 4, line 3: "command-line exceeds a length"
- Page 1264, paragraph 7, line 4: "command-lines that run"
- Page 1330, paragraph 2, line 5: "command-lines that follow"
- Page 1417, very end of page: "command-lines in this chapter"

Not sure if this matters for searches, but the second of these is in code, so it embeds an ASCII hyphen, not an en-dash (else it fails on copy/paste). Conversely, also replace the space with a hyphen in this:

- Page 1481, para 1, line 3: "command line form"

(There's also a missing hyphen in "command line arguments" on Page 466, code line 6, but don't change this one: it's Python interpreter docs output.)

Mark Lutz
 
Jun 26, 2021  Jul 02, 2021
Page p. 191
Table 7-1

The two string formatting examples in Table 7.1
"a %s parrot" % kind
"a {0} parrot".format(kind)
give an error in Idle
NameError: name 'kind' is not defined

To make examples work a quoted string is needed:
"a %s parrot" % 'kind'
"a {0} parrot".format('kind')

Note from the Author or Editor:
No, this is an abstract code snippet in a table, which uses a variable that's assumed to be assigned in actual use. It's not a complete working example, and there are other snippets like this in the same table. To run, simply assign a "kind" variable of your own.

No changes required, but keeping as a clarification for other readers.

Denis Kosygin  Oct 27, 2023 
Printed, PDF, ePub
Page 3
Last sentence of paragraph "Software quality"

I think the last words of the last sentence of the paragraph "Software quality" that now read "[...] and function programming." should be "[...] and functional programming.".

Note from the Author or Editor:
A valid typo (a term clarified ahead, but best to use accurately here too); at the very end of the referenced paragraph, please change:
"and function programming."
to:
"and functional programming."

Alexander Thaller  May 13, 2015  Jun 26, 2015
PDF, ePub, Mobi
Page 28
In paragraph "The Programmer’s View" first sentence after code

The sentence "This file contains two Python print statements,[...]" is only true for Python 2.x where print is a statement, but for Python 3.x, which the code seems to be for, the sentence should read something like "This file contains two statements which are calls to the Python print function,[...]".
On the following page 29 the first sentence after the image is "I’ll explain the print statement,[...]" for which the same as above applies.

Note from the Author or Editor:
[Not an error, no change required; retaining only as a note for other readers.]

The post's point is well taken (print is a function call in 3.X but a unique statement in 2.X), but the "statement" terminology used here is not incorrect, and was chosen deliberately to apply to both the Python 2.X and 3.X lines without requiring further elaboration so early in the book.

In truth, this code _is_ two statements in both lines. In 2.X, it's two unique print statements. But in 3.X, it's also two statements -- call expressions on lines of their own, which happen to call print. Either way, it's two print statements.

Moreover, this section pretty clearly defers to later sections for finer precision, and the book later documents its use of "statement" for print operations as an intentionally version-neutral description. Clarifying the 2.X/3.X print distinction is too much detail at this early point in the book, and is postponed on purpose.

And if that's not enough to merit a pass, describing these as "calls to the Python print function" would be incorrect for readers using Python 2.X -- this code works as is in 2.X, and the parenthesis don't make these calls in that line (they simply enclose an expression).

Hence, clarifying the 2.X/3.X semantic difference is more trouble than it's worth at this early location, and seems a minor point in any event.

Alexander Thaller  May 20, 2015  Jun 26, 2015
Printed, PDF
Page 36
2nd paragraph

"[...] and is able to created type-specific machine code [...]"

should rather read "create".

Note from the Author or Editor:
Yes -- a valid minor typo. Please fix in reprints as suggested.

Julian R.  Jan 25, 2014  May 02, 2014
PDF
Page 44
3rd paragraph

Spelling mistake "save" spelled as "shave" , in page 4 paragraph 3rd second last line.

Note from the Author or Editor:
[No edits required] The use of "shave" here and elsewhere is intentional and correct -- its somewhat informal English meaning is to reduce -- but you are not the first to ask, so I'm retaining this as a clarification for other readers.

Anuj Shrivastava  Nov 27, 2018  Mar 22, 2019
Printed, PDF, ePub, Mobi,
Page 47
Last paragraph

The line "(e.g., cd c\: and mkdir code)" should read "(e.g., cd c:\ and mkdir code)." Specifically, the c\: is wrong.

Note from the Author or Editor:
Yes -- minor, but a true typo. Please patch in the next reprint as suggesed.

Anonymous  Nov 22, 2013  Jan 24, 2014
ePub
Page 49
Not sure (I'm reading book on Kindle)

First sentence of paragraph reads: "Notice the italicized note about this on the right side of this listing (staring with “#” here)." I'm pretty certain this should read: "Notice the italicized note about this on the right side of this listing (starting with “#” here)."

Note from the Author or Editor:
Yep (I'm not sure what "staring with #" means, but I may have done so once or twice in the 70s...). Please change the "staring" to "starting" as suggested. This is on page 50, para 4, line 1 in printings on or after Sep-2016 (and para 2, line 1 in earlier printings).

Richard Bruer  Mar 02, 2017  Apr 21, 2017
PDF
Page 49
Last paragraph

Moreover, because the interactive ses-
sion automatically prints the results of expressions you type, you don’t usually need to
say “print” explicitly at this prompt:
>>> lumberjack = 'okay'
>>> lumberjack
'okay'

This suggests, "print" is implied in interactive mode. However the output is actually different if print is literally written in the statement:

>>> lumberjack = 'okay'
>>> lumberjack
'okay'

>>> lumberjack = 'okay'
>>> print (lumberjack)
okay

In the first output there are quotes, in the second there are no quotes.

Note from the Author or Editor:
[Edit]
This is actually just before the first code listing on page 50 in recent printings. At the end of this paragraph, which concludes with:
""
say “print” explicitly at this prompt:
"""
Append text to change this to (keeping the quotes formatting):
"""
say “print” explicitly at this prompt (the format of automatic prints can differ slightly, but you don't yet need to care):
"""
This will overflow the line, bit it looks like there's ample room on this page to avoid changing page breaks; please run this past me after the edit to verify.

[Discussion only follows]
Good point, and you're right that the output of print() and interactive echo isn't 100% identical in some cases (technically, this is the difference between Python's repr and str displays, as covered later in the text). But the point of the narrative at this early location in the book was just that your results are automatically displayed at the interactive prompt, without requiring you to code a formal print().

This differs in files, and it's common for beginners to miss the distinction. The book here does not claim that the output format for echoes and prints is always exactly the same, and explaining the subtle reason why only the former display quotes for strings seems far too much detail on page 49.

That said, it's worth the simple insert above to head off potential confusion over this. It also seems worth retaining this post for any other readers with the same query (with type changed to "clarification;" this isn't a mistake).

For more on the subtle echo/print() formatting difference, be sure to see the sidebar "str and repr Display Formats" on page 149; the coverage of print() in Chapter 12; and the later coverage in the Classes and OOP part of the book for the __repr__ and __str__ methods which implement the two output formats. As a tutorial, this book's best advice almost always is to "read on."

E. Mehmet Yusuf  Nov 15, 2020  Dec 18, 2020
PDF
Page 56
4th paragraph (Paragraph begins: "If it all works as planned, this shell command...")

Sentence in question:

"If all works as planned, this shell command makes Python run the code in this file line by line, and you will see the output of the script’s three print statements—the name of the underlying platform as known Python, 2 raised to the power 100, and the result of the same string repetition expression we saw earlier (again, more on the meaning of the last two of these in Chapter 4)."

Phrase in question:

"as known Python"

Should this be changed to: "as known by Python" ?

I did not see this error posted on the errata page.

Your book is really great!!! Thank you so much!

-Ted

Note from the Author or Editor:
Yes, a valid typo -- please change the referenced clause from "as known Python" to "as known to Python".

Edward (Ted) Zhu  Jun 16, 2015  Jun 26, 2015
Printed, PDF, ePub, Mobi,
Page 58
4th parageaph

"In Python, using a variable before it has been assigned a value is always an error?
otherwise, if names were filled in with defaults, some errors might go undetected. This
means you must initial counters to zero before you can add to them, must initial lists"

In the last line above, the transitive verb use of the word "initial" means to mark with your initials. The word used should be "initialize", the definition of which is referred to in the first line above.

Note from the Author or Editor:
[This is actually on Page 51 in print and PDF, not 58]
Yes -- a typo. Change:
"This means you must initial counters to zero before you can add to them, must initial lists before extending them, ..."

to this, changing the two "initial" to "initialize":
"This means you must initialize counters to zero before you can add to them, must initialize lists before extending them, ..."

[Discussion only follows] This typo isn't serious given that the terms aren't formal here, and the intent is spelled out fairly clearly in the remainder of this sentence -- "; you don?t declare variables, but they must be assigned before you can fetch their values".

Still, "initialize" is the better term. In fact, "initialize" does appear correctly 60 times in the book, and the term "must initialize" itself is used properly in 3 other places (page 176, 349, and 516). Given that, and the fact that a similar typo appears in the 3rd but not 4th edition, a Word auto-correct seems a prime suspect here.

eatyourhat  Sep 30, 2013  Nov 08, 2013
Printed
Page 62
4th line of 3rd paragraph in section named "Icon-Click Basics"

Change "files without a console" to "without a console"

Note from the Author or Editor:
[On page 63 para 4 line 4 of printings on or after Sep-2016]
Yes--this is a bogus repeated word. Please remove the second "files" in the clause "and .pyw files are run by pythonw.exe files without a console".

Nick Reeder  Nov 07, 2016  Jan 06, 2017
PDF
Page 67
3rd code listing

This line of code is deprecated:
from imp import reload

According to python doc:
Deprecated since version 3.4: The imp package is pending deprecation in favor of importlib.

Please use this instead:
from importlib import reload

Note from the Author or Editor:
[NO EDITS REQUIRED]

True, but the suggested and newer "from importlib import reload" does not work in Python 3.3 and earlier -- this edition's target versions -- so this can't be used, and can't be easily noted given the subject page's size constraints. Still, I'm retaining this as a general note for future readers of this page.

General remarks: This book cannot be updated for changes to Python that occurred after its publication. Such changes are largely the domain of future editions. In this case, Python 3.3's documentation does not label either imp or imp.reload() as deprecated, and the code given -- "from imp import reload" -- works fine as described, and should continue working for a number of Python releases to come. Hence, this doesn't merit a patch in the current edition.

Although this book stresses fundamentals that span Python versions, Python is prone to change frequently and arbitrarily, and book readers should expect to browse Python's "What's New" changes lists periodically for recent mutations. Changes are usually minor (like this post's subject), but monitoring them is a normal part of using such a tool.

Ahmad  Jul 15, 2015  Sep 04, 2015
Printed
Page 72
Code sample at bottom of page

The code sample suggests it is being run from Linux (note the "%" command prompt); yet the output of "sys.platform" is still "win32". For consistency, this should probably be "linux".

Note from the Author or Editor:
[No changes required, not errata, informational note only; this is on page 74 in recent printings]

Thanks for your note; I'm retaining this as a clarification for other readers, but dismissing it as not-an-errata for three reasons: this example's section does not mention Linux at all, and it is not implied; the "%" prompt is very clearly documented as a generic (i.e., platform-neutral) system prompt earlier in this chapter on pages 44 and 48; and this example is run the exact same way--with a "%" prompt and Windows output--on page 56. That doesn't qualify as inconsistent.

Having said that, in retrospect I sometimes regret that more examples were not demonstrated on Linux or Mac OS. In fact, I use Python on Mac OS almost exclusively these days myself, with grudging ports to Windows (see http://learning-python.com/programs.html for recent code). For better or worse, though, Windows was and remains dominant in terms of user base. Luckily, one rarely needs to care about the underlying platform when running the pure-language Python examples in this book.

Ben Hekster  Sep 09, 2017  Oct 27, 2017
Printed, PDF, ePub, Mobi, , Other Digital Version
Page 75
second paragraph of section "IDLE Basic Usage"


IDLE Basic Usage
Let?s jump into an example. Figure 3-3 shows the scene after you start IDLE on Win-
dows. The Python shell window that opens initially is the main window, which runs
an interactive session (notice the >>> prompt). This works like all interactive sessions
?code you type here is run immediately after you type it?and serves as a testing and
experimenting tool.
IDLE uses familiar menus with keyboard shortcuts for most of its operations. To make
a new script file under IDLE, use File&#8594;New: in the main shell window, select the File

In the last line above, the command in Python 2.7.4 is File-New Window

Note: Helping improve the book is the least I can do for free use of one so good. Thanks!

Note from the Author or Editor:
Yes -- for full accuracy, this should probably use "New Window", not just "New". This is true of IDLE in both Python 3.X and 2.X today, at least in 3.3.2 and 2.7.5.

However, it turns out that this menu label will be changed to instead say "New File" in Python 3.3.3 and 2.7.6. This is despite common GUI practice; any existing IDLE documentation; and the fact that it's really just a new text _editor_ window, and not associated with any _file_ at all unless and until you open or save the window's text. Better, perhaps, but this still seems to have just as much potential to confuse newcomers!

[THE BOOK CHANGE] This report's type was changed to typo, because most readers will likely get the point anyhow; "New" is the full option name's prefix in all Pythons, and common usage in GUIs. But to clarify, let's change sentence 2 of paragraph 2 in this section from this (which may show up here with an embedded arrow symbol given as an "&" escape):
"""
To make a new script file under IDLE, use File&#8594;New: in the main shell window, select the File pull-down menu, and pick New to open a new text edit window where you can type, save, and run your file?s code.
"""

To this, replacing the two "New" as shown (retaining existing italics on the first 6 words, and adding a "that is," for clarity, since this page has the space):
"""
To make a new script file under IDLE, use File&#8594;New Window: that is, in the main shell window, select the File pull-down menu, and pick New Window (New File as of 3.3.3 and 2.7.6) to open a new text edit window where you can type, save, and run your file?s code.
"""

[MORE DISCUSSION] This section was revamped substantially in the 5th edition, and in the process the menu option name was abbreviated in this paragraph only (on the assumption that the standard menu name prefix would suffice, and be more immune to change like that in 3.3.3). The full "New Window" is still given correctly in the caption to Figure 3-3, a holdover from prior editions whose coverage of IDLE basics was also a bit less useful. This is a minor detail, of course, but important to get right at this early stage of the text.

eatyourhat  Oct 02, 2013  Nov 08, 2013
Printed
Page 77
last but two paragraph

provides more advanced features, including a point-and-click program graphical de-
bugger and an object browser. The IDLE debugger is enabled via the Debug menu and
======
should be a path browser:

provides more advanced features, including a point-and-click program graphical de-
bugger and a path browser. The IDLE debugger is enabled via the Debug menu and
====

Note from the Author or Editor:
This text is on page 79 on more recent printings, paragraph 2. This may be picking nits, but in the referenced paragraph, let's change the start of sentence 3 from:
"The browser allows you to navigate through" to:
"The browser allows you to both inspect classes and navigate through"

[Discussion only follows] The latest IDLE has both a "Class browser" and "Path browser" in its File menu, but both ultimately are ways to navigate and browse Python objects. Hence "object" as generic term is not inaccurate here.

Less arguably, IDLE's notion and implementation of an object browser has morphed over years and Python releases (and may very well do so again in the future), so it's best to leave this is generic as possible. Using "object" also avoids having to expand other references in this paragraph.

This is not, by the way, a "serious technical mistake"--especially given that this is just an IDLE menu option mentioned only in passing here, and never again. Category changed.

Eckhard Stein  Sep 03, 2017  Oct 27, 2017
Printed
Page 84
Last line of fourth bullet

Change "example or running" to "example of running"

Note from the Author or Editor:
[On page 86 line -8 in printings on or after Sep-2016]
Yes; please change "example or running pdb" to "example of running pdb".

Nick Reeder  Oct 28, 2016  Jan 06, 2017
Printed
Page 98
6th text paragraph, line 1

The word "random" is strangely broken across 2 lines. At the least, it should have a hyphen at the break. Even better, because it is in different typeface, it probably shouldn't be broken at all, so that the whole word is on one line.

Note from the Author or Editor:
Reprints: please remove the line break in the middle of this "random" literal (by forcing the entire word to the next line, probably). This one is so short that it shouldn't have a noticeable impact on the surrounding lines, and there are no later literals in this paragraph that may split as a result.

Discussion: whether official style or not, line-breaks in the middle of literals are, unfortunately, common in O'Reilly books. I've raised this issue on each of mine, and pointed out the most grievous for manual repair, but some seem to slip through the cracks anyhow. The rationale is that long literals may cause prior/next lines to be too sparse or choppy if not split, and the shifting that results from fixing one may cause other later literals to be split anyhow. In this case, however, the split literal is so short that the effect of removing the line break will be harmless.

Mitch Butler  Jul 29, 2014  Aug 22, 2014
Printed, PDF, ePub, Mobi,
Page 102
code comments

# B[i] = ord(c) works here too
ord(c) should be ord('c')

Note from the Author or Editor:
CHANGE this comment's text (in line 10 of listing 1) from:
... # B[i] = ord(c) works here too
to this, (replacing just "c" with "x"):
... # B[i] = ord(x) works here too

DISCUSSION: This isn't an errata, and I nearly dismissed it altgether, but opted to retain it with a minor tweak and note, and reclassify as a clarification.

I understand how this may seem confusing, if compared to the "L[1] = 'c' " given for a list above this. However, this is comment, not concrete code, and was meant as abtract only -- it means for some integer "i" and some character "c", "B[i] = ord(c)" works.

Either this should be fully concrete (using 1 and 'c') or fully abstract (using i and c, as given); both would be valid, but changing just the right part to be concrete isn't enough. On the other hand, the "c" is the point of confusion, so we'll change this to use a dfferent variable name to make the abstractness more obvious, and render this moot.

Anonymous  Apr 28, 2014  Aug 22, 2014
Printed
Page 102
2nd text paragraph

The word "bytearray" in line 2 is strangely broken across 2 lines. At the least, it should have a hyphen at the break. Even better, because it is in different typeface, it probably shouldn't be broken at all, so that the whole word is on one line.

Note from the Author or Editor:
Reprints: try to remove the line-break in the middle of this literal (by forcing it to the next line?), but only if doing so does not stretch/empty the surrounding lines too much, cause similar literal line-breaks later in this paragraph, or degrade formatting in this paragraph in general.

Discussion: whether official style or not, line-breaks in the middle of literals is, unfortunately, common in O'Reilly books. I've raised this issue on each of mine, and pointed out the most grievous for manual repair, but some seem to slip through the cracks anyhow. The rationale is that long literals may cause prior/next lines to be too sparse or choppy if not split, and the shifting that results from fixing one may cause other later literals to be split anyhow. I'm marking this one for possible repair, but only if it doesn't harm other formatting; in this case, it will leave a dash at the end of the prior line, and might introduce a new line-break in another literal later in this paragraph.

Mitch Butler  Jul 29, 2014  Aug 22, 2014
Printed
Page 102
Paragraph 5 beginning "Probably the easiest way to think of slices is..."

5th Edition, copyright 2013.

In paragraph 5, the fourth sentence is referring to the previous code segment (s[1:3]) when it says, "The second of the preceding operations, for instance, gives us all the characters in string S from offsets 1 through 2 (that is, 1 through 3-1) as a new string". The reader is left thinking that this is the general algorithm for slice interpretation. However, this wording is misleading, and only correct because the first offset in the slice is 1. A more general description would be to say that this expression gives you 2 (3-1) characters, starting at position 1.

The example in the next paragraph (paragraph 6) of S[0:3] emphasizes the point. It shows that S[0:3] returns 'Spa' This time, the first offset is 0, and the result is not "all characters between 0 and 3 (3-0)". That would be 'Spam', which is wrong. Instead, it returns 3 (3-0) characters, starting at position 0. This is 'Spa', and the example correctly shows this result.

Note from the Author or Editor:
[No changes required: informational only]

No, this is correct as given (and it's certainly not a "minor technical mistake": category changed, but post retained for context).

This post lays out an alternative (if arguably convoluted) way to interpret some slice examples' limits, but that doesn't make the existing description invalid. The main point in this section is that the upper bound is non-inclusive--by far the slice's most confusing property to Python newcomers; hence the "- 1".

Frankly, this post is reading too much into one sentence in a larger section in an overview chapter, and picking unwarranted nits. Slicing is described correctly both here, and in more detail later in the book; please reread. That said, if these examples inspire some readers to find their own way to look at slices, they're doing their job.

Glenn Graessle  Apr 09, 2017  Apr 21, 2017
Printed
Page 104
Second paragraph beginning, "Strictly speaking, you can change text-based data in place..."

The code block starting with "S = 'shrubbery' is an example of how text-based data can be changed in place. It does this by creating a list using S, changing that list in place, and then joining the list with '' .join() to create a string again.

However, when the ''.join() is run, and the interpreter prints 'scrubbery', the reader is led to believe that S was somehow changed in place. That is obviously wrong, but why then use ''.join() in the example at all. It seems to be less confusing to enter L again, which shows that the list was changed in place, and that the original string was not somehow involved. .

Note from the Author or Editor:
[No changes required: informational only]

No, this is not obviously (or otherwise) wrong. As it states explicitly, this example is about changing *text-based data* in-place in general, not the Python str type.

The latter is indeed immutable, but "text" is a much more general concept, spanning content read from files, scraped from web pages, and so on. Programmers do need to change such text "in-place" even though it may have been extracted as a str type object, and this section surveys two techniques for doing so. It is correct as given.

I understand the poster's point, and it is valid under one strict interpretation (and I'm retaining this post for anyone else who may be deploying such a strict interpretation). But this was not about str immutability, which is amply illustrated both in this same section and elsewhere in the book. Its was about text-processing techniques. The bytes-type example here should be enough to underscore that this was so.

Glenn Graessle  Apr 09, 2017  Apr 21, 2017
Printed, PDF, ePub, Mobi,
Page 105
last line of 1st code listing section on page

Reported by a sharp-eyed reader's email: the first character in this output line should be uppercase "A", not "a" (a minor typo missed by many, but potentially confusing nonetheless).
Change:
>>> S # ...
'a\x00B\x00C'
to:
>>> S # ...
'A\x00B\x00C'
retaining the "# ..." comment's text and its indentation verbatim.

Mark Lutz
 
Apr 05, 2014  May 02, 2014
PDF
Page 105
Last paragraph

Shouldn't

"but allow the other type of quote to be embedded with an escape"

be

"but allow the other type of quote to be embedded without an escape"

After all, the following works:

a = '"' # this is a single quote followed by a double quote followed by a single quote

Note from the Author or Editor:
Yes, a genuine typo (in a brief mention of a subject that is covered in full later in the book: see the section on Page 193). Change as described -- Page 105, last paragraph, end of line 2, from the first of these to the second:

"to be embedded with an escape (most"
"to be embedded without an escape (most"

Jon Forrest  Sep 28, 2014  Oct 29, 2014
Printed
Page 105
Comment in fourth line of code listing

Should "the binary value 10" be "the decimal value 10"? The ASCII code for newline is decimal 10, which is binary 1010. Or am I misunderstanding your intent? Similarly for the answer to question 6 on page 238 (should "binary 31" be "decimal 31"?). Also for two comments in the code listing on page 1167 (should "binary value 97" be "decimal value 97"?) and to the second line above that code listing.

Note from the Author or Editor:
[On pages 108, 246, and 1208 in printings on or after Sep-2016]
This is a bit grey, but to avoid potential confusion, change the referenced items from the first to the second in each of the following, retaining the spacing before "#":

1) Page 108 - from/to:
# \n is a byte with the binary value 10 in ASCII
# \n is one character coded as decimal value 10

2) Page 246, #6- from/to:
binary 31 (a hex escape \x1f), binary 0
literal value 31 (a hex escape \x1f), literal value 0

3) Page 1208 - from/to:
# 'a' is a byte with binary value 97 in ASCII (and others)
# 'a' is a byte coded as value 97 in ASCII (and others)

4) Page 1208 - from/to:
# Binary value 97 stands for character 'a'
# Code value 97 stands for character 'a' in ASCII

[Discussion only follows]
The use of "binary" here was not meant to mean a base-two value, but a literal in-digital-memory value. I can understand the potential confusion in the first instance, where a "10" value might look like a base-two value, but not in the other two, where the "31" and "97" values can't possibly be misinterpreted as base-two values (they're not zero and one digits, after all). Still, this has the potential to confuse -- especially in the first instance -- so I'm marking it for changes as described.

Perhaps more significantly, the use of "byte" is also not ideal here in retrospect: "character" or "code point" are more appropriate in Python 3.X's Unicode world, though it's not too misleading when referring to ASCII, and is accurate for Python 2.X's byte-based string type. Really, the meaning of a numeric value in a string literal varies subtly between Python 2.X and 3.X (it's an absolute byte-value in the former and a Unicode character's code point in the latter), and syntax and display both vary:

~$ py3
>>> s = 'a\n\xC5\u2606\U00002606'
>>> len(s), s
(5, 'a\nÅ☆☆')
>>> list(s)
['a', '\n', 'Å', '☆', '☆']

~$ py2
>>> s = 'a\n\xC5\u2606\U00002606'
>>> len(s), s
(19, 'a\n\xc5\\u2606\\U00002606')
>>> list(s)
['a', '\n', '\xc5', '\\', 'u', '2', '6', '0', '6', '\\', 'U', '0', '0', '0', '0', '2', '6', '0', '6']

But this is firmed up later in the book (see page 1180/1220 for a random example), and is far too much to try to clarify earlier in reprints.

Nick Reeder  Oct 29, 2016  Jan 06, 2017
Printed, Mobi
Page 108
P4, s3: The following pattern, for example, . .

The narrative describes the example following it incompletely. In the sentence beginning "The following pattern, for example, . . . " change "separated by slashes, and is similar" adding "or colons" thus: "separated by slashes or colons, and is similar"

Note from the Author or Editor:
Yes -- change as described. This seems minor, given that the re module isn't covered by this book (as it states), and the example's full behavior is implied by the listing immediately following this sentence; but it's worth tightening up the description this way. This reflects an expansion of this example in this edition: the alternatives syntax and re.split equivalent were added for color, but the narrative wasn't updated for the former.

Christopher Karl Johansen  Jul 18, 2013  Aug 16, 2013
Printed, PDF, ePub
Page 108
First sentence of 2nd paragraph of section Pattern Matching

The sentence currently reads:
This example [...] the word “Hello,” followed by [...] the word “world.”
The problems are that the comma inside "Hello," and the period inside "world." have to be taken outside the quotation marks and changed to "Hello", and "world". respectively.

Note from the Author or Editor:
[NO EDIT REQUIRED] This has been raised a few times in the past (and I'm retaining this as a note for future page readers). I agree that this looks odd, but it's normal publishing style to place the comma and period inside the immediately preceding quote this way. In other words, it's deliberate, and I doubt O'Reilly would be able to bend the universally-followed rules on this.

Alexander Thaller  Jun 24, 2015  Sep 04, 2015
Printed
Page 108
bottom

On FreeBSD and NetBSD using python3.6, the transformation of 'sp\xc4m' to include an umlauted A didn't work. I was able to get it to work by setting environment variable LANG to en_US.UTF-8. On NetBSD (where I have access to more versions of python3), (a) the same applies to python3.4 and python3.5, and (b) it seems to be fixed in python3.7; there it isn't necessary to set LANG. Maybe you don't want to go into all those details in the text, but you might want to say something like: "For best results in some cases, set LANG to en_US.UTF-8."

Note from the Author or Editor:
[No changes required] Good point, and I'm retaining this post as a clarification for other readers having the same issue, but this is too large a topic for us to insert at this location in the book.

A quick check shows the example works as shown and without extra environment settings on Mac OS with Python 3.5; on Android with Python 3.7; and of course on the Windows machine used during book development. In all cases tested:

>>> 'sp\xc4m' # 3.X: normal str strings are Unicode text
'spÄm'

That said, the Unicode defaults story in Python seems to have been in flux since the book was published, and some related Windows code-page settings are covered along the way (e.g., page 779). A more complete description that addresses all platforms and Pythons, though, is regrettably beyond this edition's scope.

Bill Evans  Nov 03, 2019  Nov 22, 2019
Printed
Page 111
about 1/4 page from the top

The line:

>>> re.split('[/:]', '/usr/home/lumberjack')

would be more instructive if it instead was:

>>> re.split('[/:]', '/usr/home:lumberjack')

Note from the Author or Editor:
This is not an error; the example works as shown, and demoes a split where one of the alternatives is not present in the source string. Still, I agree that it might be marginally better to use the same source string as the code just above this, for symmetry. So, let's change the referenced "/usr/home/lumberjack" to "/usr/home:lumberjack" as proposed.

Bill Evans  Oct 26, 2019  Nov 22, 2019
Printed
Page 114
First and second lists of code and last line of middle paragraph.


Code samples to get matrix list by columns. But instead the code has ''row'.

i.e.
Instead of "col2 = [row[1] for row in M]"
it should be
"col2 = [col[1] for col in M]"
for all code references for 'row'.



I

Note from the Author or Editor:
[No changes required] The use of "row" was intentional here. The list comprehension is really stepping though a list of rows, not a column, and its "row[1]" means the second item in each row along the way. Hence, the collection of these gives the second column in the matrix, as described. If something else was intended in your post, please follow up; else, retaining this as a clarification for other readers. [This is on page 111 in earlier printings.]

Steve Sowder  Mar 10, 2019  Mar 22, 2019
Printed, PDF, ePub, Mobi,
Page 120
3rd paragraph, insert at end

On Page 120, extend the end of paragraph 3, which concludes with:
"""
tools like range and map.
"""
to read as follows, adding a new sentence (and retaining the literal font on "range" and "map"):
"""
tools like range and map. By deferring results until needed, these tools can both save memory and minimize delays.
"""

We appear to have room on the page for the insert, and a brief mention of the "why" of iterables seems sorely needed here, even though it's covered in depth later in the book.

Mark Lutz
 
Jan 05, 2015  Jan 09, 2014
Printed, PDF, ePub, Mobi,
Page 122, 278
122: end of 1st paragraph, 278: end of 3rd paragraph

I'd like to add two short inserts to clarify unparenthesized tuple syntax. It can lead to fairly nasty syntax errors, in constructs such as lambda bodies. This is addressed elsewhere, but could use a bit of elaboration at the insert locations. The two inserts:

1) Page 122, end of 1st paragraph. Change:
"
(the parentheses enclosing a tuple?s items can usually be omitted, as done here):
"
to the extended:
"
(the parentheses enclosing a tuple?s items can often be omitted, as done here; in contexts where commas don't otherwise matter, the commas are what actually builds a tuple):
"

2) Page 278, end of 3rd paragraph. Change:
"
Many programmers (myself included) also find that parentheses tend to aid script readability by making the tuples more explicit and obvious, but your mileage may vary.
"
to the shortened:
"
Many programmers also find that parentheses tend to aid script readability by making the tuples more explicit and obvious*.
"
where the * references a new footnote at the bottom of this page, with text:
"
*A subtler factor: the comma is a sort of lowest precedence operator, but only in contexts where it's not otherwise significant. In such contexts, it's the comma that builds tuples, not the parenthesis; this makes the latter optional, but can also lead to odd, unexpected syntax errors if parentheses are omitted.
"

If this changes page breaks, please ask me to shorten the wording (I've shortened the existing text to try to avoid breaking the current content across pages).

Mark Lutz
 
Aug 07, 2014  Aug 22, 2014
Printed
Page 127
2nd group of interpreter session input/output

I believe the comment at the end of one of the lines:
# Order-neutral equality tests (== is False)
should be:
# Order-neutral equality tests (!= is False)

Note from the Author or Editor:
Reprints: in the referenced comment text, please change:
# Order-neutral equality tests (== is False)
to:
# Order-neutral equality ('spam'=='asmp' False)
using straight quotes, and keeping all other text unchanged, including the preceding whitespace used for "#' vertical alignment. I deleted the word "tests" so that this still fits on one line.

Discussion: Not an error and just a comment, but I agree this is a bit terse and worth a minor clarification. The intent here was that running strings through set() makes them compare equal (order neutral), while comparing the strings themselves is False (order matters). This is called out in more detail in the next chapter's fuller coverage of sets, where the same example makes this more obvious, and also compares the finding to sorts (Page 170):

>>> 'spam' == 'asmp', set('spam') == set('asmp'), sorted('spam') == sorted('asmp')
(False, True, True)

Interestingly, the "is" identity test is False for these sets too (they have same value but are different objects), but this operator isn't introduced until the next chapter. Per the post, "!=" is False for the sets as well, but that's just the inverse of what's shown, and wasn't the comment's intent about the underlying strings.

Mitch Butler  Jul 29, 2014  Aug 22, 2014
PDF
Page 127
last code example for fraction

In the last example code for fraction number
>>> from fractions import Fraction # Fractions: numerator+denominator
>>> f = Fraction(2, 3)
>>> f + 1
Fraction(5, 3)
>>> f + Fraction(1, 2)
Fraction(7, 6)

The last statement should be "f - Fraction(1,2)" instead "f + Fraction(1, 2)" to produce the result "Fraction(7,6)

Note from the Author or Editor:
[No change required: informational only]

No, the example works correctly as shown above and in the book. Your suggested change, "f - Fraction(1, 2)" would instead yield "Fraction(1, 6)", the result of 4/6 - 3/6.

Probably, you are assuming that the variable "f" is changing to the result values as the example proceeds. Like all names in Python, it does not, unless it is explicitly assigned at each step, or references a mutable object whose values can be changed in-place (Fractions cannot). Even in the latter case, the name doesn't change, only the object it references. In the following, for example, "f" remains Fraction(2, 3) throughout:

>>> from fractions import Fraction
>>> f = Fraction(2, 3) # make a new object
>>> f + 1 # result is a new object
Fraction(5, 3)
>>> f + Fraction(1, 2) # but result not assigned to f!
Fraction(7, 6)

If you do assign the result back to "f" the example works as you expected. This is not what was demonstrated in the book, but this sort of thing trips of newcomers often enough that I'm retaining this reply as a note:

>>> f = f + 1 # make f reference a different value (object)
>>> f - Fraction(1, 2) # now it is 5/3 - 1/2 (10/6 - 3/6)
Fraction(7, 6)

Hongsoog Kim  Jan 19, 2017  Apr 21, 2017
Mobi
Page 131
text

CURRENT:
Instead, use a simple for loop instead, without range or counters, whenever possible; it will be easier

SUGGESTED FIX:
"Instead, use a simple for loop, without"

Note from the Author or Editor:
REPRINTS: Yes - please change as described, deleting the redundant second "instead" before the comma at the end of the clause. This is near the end of solution #4 on page 430 in recent printings (and page 414 earlier).

Anonymous  Dec 16, 2019  Jun 05, 2020
PDF
Page 137
Table 5-2

Operator precedence is grouped differently in the official documentation (e.g. https://docs.python.org/3.7/reference/expressions.html#operator-precedence) vs. in Learning Python 5th Edition (LP5E). Mostly this is not consequential for regular math, but the division and remainder operators must be listed on the same level of precedence, as described in the official documentation. As written, LP5E describes a different behavior for expressions like the following:

>>> 127 % 20 // 5
Actual result (Python 3.6.8) equivalent to (127 % 20) // 5:
1
LP5E describes the division as having higher precedence than modulus, resulting in an equivalent to 127 % (20 // 5):
3

Note from the Author or Editor:
UPDATE: as described in the original reply below, the formatting of Table 5-2 didn't reflect operator precedence as formally as it might have. We've improved this in the November 2019 reprint by (1) adding borders around table cells to make row membership (and hence precedence level) clearer; and (2) coalescing a few adjacent but separate rows for better accuracy. See the before and after pictures here:

https://www.learning-python.com/LP5E-Table-5-2-Before.png
https://www.learning-python.com/LP5E-Table-5-2-After.png

You can find much more about this change, including both text and PDF renditions of the reformatted table, at this note:

https://www.learning-python.com/book-queries-lp5e.html#q_8

----original reply follows----

Short story: Table 5-2 generally matches Python's docs, but its formatting of a "row" isn't as obvious as it could be, and full accuracy requires coalescing a few adjacent rows.

Details:
This is regarding Table 5-2, which appears on page 141 in recent reprints. This is not an error -- the book's precedence actually matches that of Python's docs, but the table's original formatting of a "row" may leave a bit to be desired, and its content might be less formal than some readers would like.

As is, rows in Table 5-2 are distinguished by having just a little more whitespace between them than their members. For example, the operators in question all belong to the same Table 5-2 row (which includes "@" in 3.5+, not covered in the book) like this:

x * y
x % y
x / y, x // y

Which are formatted the same as the "+" and "-" row that appear as follows above them (and are hence of lower precedence):

x + y
x – y

The book further states on page 143:

* Operators lower in the table have higher precedence, and so bind more tightly in mixed expressions.
* Operators in the same row in Table 5-2 generally group from left to right when combined (except for exponentiation, which groups right to left, and comparisons, which chain left to right).

Thus, the "127 % 20 // 5" should group as "(127 % 20) // 5" per this definition -- as it does. The real issue seems to be that the notion of row membership in Table 5-2 isn't obvious. I seem to recall that we struggled with this in production and the extra spacing was deemed sufficient, but this may not be so. By comparison, Python Pocket Reference uses commas between row members to associate them marginally better, but this would lose in-row semantics in Learning Python's version, and doesn't seem an adequate fix.

Though not mentioned by the errata post, some other Table 5-2 operators also appear to be in different rows when they shouldn't, and may merit tweaks (e.g., "is" and "<" should be in the same row, as should +x, -x, and ~x). On the other hand, I'm not sure this was always crisp in Python docs, and unparenthesized combinations of such operators seem very unlikely.

In any event, thanks for your report, and we'll update this note after exploring options for improvement.

Jeff Spirko  Jul 05, 2019  Nov 22, 2019
Printed, PDF, ePub, Mobi,
Page 138
Insert at end of 5th bullet item

A minor insert to further clarify how commas work in unparenthesized tuples. At the end of the 5th bullet item on this page (that begins "The syntax (...)"), add this single sentence, with its "comma" in italics:
"
When a tuple's parentheses are omitted, the comma separating its items acts like a lowest-precedence operator if not otherwise significant.
"

[Discussion only follows]
This is trivial when this is intended or appears standalone, but can lead to odd syntax errors in contexts where the tuple may not have been expected (e.g., in lambda return expressions). It could be argued that the comma is an operator in general, but it's a special case, as it serves this tupling role only in cases where it's not otherwise significant. It's really specific top-level separator syntax that doesn't work in contexts like function argument lists.

A new clause and footnote on pages 122 and 278 address this as well, but I'm also propagating the insert here from Python Pocket Reference 5th Ed. This insert adds 2 lines, but there seems ample space on the page.

Mark Lutz
 
Aug 18, 2014  Aug 22, 2014
PDF
Page 142
1st code sample

The text shows the following:

>>> b * 3, b / 2 # Multiplication (4 * 3), division (4 / 2)
(12, 2.0)

Shouldn't the result be
(12, 2)

This is what I see with Python 2.7.7

Note from the Author or Editor:
No, and this is getting a bit redundant, as the 2.X difference is mentioned before this example and described again on the very next page. But to avoid such understandable confusion -- on Page 142, 1st code listing, line 3, expand the comment test on the right from the first of the following to the second, retaining the "#" vertical alignment:

"# Multiplication (4 * 3), division (4 / 2)"
"# Multiplication (4 * 3), division (4 / 2, 3.X result)"

[Discussion only follows]
I'm marking the edit grudgingly, because it gets tedious to see "3.X" called out everywhere a trivial output differs from 2.X, especially when the difference is explained both earlier and later in the text. In this case, Page 138 (before this example, which is just one among many) states:
"""
The X / Y expression performs true division in 3.X (retaining remainders) and classic division in 2.X (truncating for integers). See “Division: Classic, Floor, and True” on page 146.
"""
And again on the very next page following the example (Page 143, shortly before an entire section on the subject on Pages 146-150), the book says:
"""
Also, notice that all the numbers are integers in the first expression. Because of that, Python 2.X’s / performs integer division and addition and will give a result of 5, whereas Python 3.X’s / performs true division, which always retains fractional remainders and gives the result 5.0 shown.
"""
Hence category was changed from technical mistake to clarification, and this is likely the last edit we'll make of this kind. I appreciate such posts and understand their intent, but examples can't generally be understood in isolation from their surrounding context and narrative.

Jon Forrest  Oct 05, 2014  Oct 29, 2014
Page 143
chapter 5

In your excellent book “Learning Python” (Mark Lutz), you referred to following problem:

(Chapter 5, p 143, numbers in action):
“We met this phenomenon briefly in the prior chapter, and it’s not present in Pythons
2.7, 3.1, and later. The full story behind this odd result has to do with the limitations
of floating-point hardware and its inability to exactly represent some values in a limited
number of bits. Because computer architecture is well beyond this book’s scope,
though, we’ll finesse this by saying that your computer’s floating-point hardware is
doing the best it can, and neither it nor Python is in error here.”

Currently I am using Google Colab (Python version 3.6) and get results like:
A = 3.1
B = 3
C = A – B
(C == 3.1)

Returns ‘False’

C returns: 0.10000000000000009

Could you give more information on you to solve this issue. I am more experienced with eg Matlab, where I would expect to have returns resp. True, and 3.1. Or is this something “normal” for Python?

Thank you much.

Note from the Author or Editor:
[No edits required] This shows up on page 150 in later printings and 146 earlier. It's arguably a minor omission from the book as is - it shows how floating-point comparisons are inaccurate, but doesn't give much detail on a workaround, apart from int(), and a comment that mentions round() and floor() and the like.

It's also too big a topic for this page, but in brief, the most obvious way to equate some operands' precisions is to round them uniformly:

>>> 1.1 + 2.2 == 3.3
False
>>> round(1.1 + 2.2, 1) == round(3.3, 1)
True

The more-exotic numeric types described later in this chapter can help too, though they may be more work:

>>> from decimal import Decimal
>>> Decimal('1.1') + Decimal('2.2') == Decimal('3.3')
True
>>> from fractions import Fraction
>>> Fraction(11, 10) + Fraction(22, 10) == Fraction(33, 10)
True

After the book was published, Python 3.5 also added a math.isclose() for this purpose, though it emulates '==' only (e.g., add an 'or x > y' for 'x >= y'):

>>> import math
>>> math.isclose(1.1 + 2.2, 3.3)
True

This new call is also a bit convoluted in some uses cases; see the docs:

https://docs.python.org/3.8/library/math.html#math.isclose

There's no room to change the book for all this, but we'll keep this post as information for other readers.

Frederic Heereman  Mar 12, 2022  Jul 15, 2022
Printed, PDF, ePub, Mobi, , Other Digital Version
Page 144
line 7 of first code listing on page

Minor formatting fix: the second line of the following on this page should not be in italics font; it should be straight, non-bold, and fixed-width like all other output text:

>>> '%e' % num # String formatting expression
'3.333333e-01'

Mark Lutz
 
Jul 11, 2013  Aug 16, 2013
Printed
Page 156
1st paragraph

The phrase 'round rounds and drops decimal digits but still produces a floating-point number in memory' is at best misleading. It may apply narrowly to the example that follows, but the docstring for round states that for round(number, ndigits=None), "The return value is an integer if ndigits is omitted or None."

Note from the Author or Editor:
[EDIT] This is at the end of the first paragraph on page 161 in newer printings. Please delete "floating-point" in the referenced clause "but still produces a floating-point number in memory".

[DISCUSSION] True, but this text has been taken out of context. The text is correct for the example shown; the one-argument variant is plainly illustrated by example just before this text; this reflects a minor change in Python 3.X only; and this book is a tutorial, not a strict reference.

Today, omitting the digits argument in round() renders an integer in 3.X as you noted, but not in 2.7, which the book also explicitly covers:

~$ python
Python 2.7.16 (default, Jun 5 2020, 22:59:21)
>>> round(1.234, 0)
1.0
>>> round(1.234)
1.0

~$ python3
Python 3.9.1 (default, Dec 9 2020, 13:22:01)
>>> round(1.234, 0)
1.0
>>> round(1.234)
1

Though the history is sketchy, round() seems to have changed a few times in 3.X. 3.0 adopted the practice of returning an integer when called with just one argument, though this is allowed to vary per type in its x.__round__([n]) callback. 3.1 also changed round() to return an integer for an integer argument (see docs.python.org/3/whatsnew/3.N.html, where N is a release number).

That said, these are fairly small reference-level details, and the actual example discussed and shown in this section does produce the same floating-point result in both the 2.X and 3.X lines:

~$ python2 or python3
>>> round(1 / 3.0, 2) # same in both 2.X and 3.X
0.33

Moreover, page 155 (161 in new printings) does show the 3.X integer round() result for one argument immediately before the text in question, which probably should suffice at this point in the book:

>>> round(2.567), round(2.467), round(2.567, 2) # Round (Python 3.X version)
(3, 2, 2.57)

In any event, I doubt this qualifies as "misleading at best." The text is correct as is for its context, and readers should expect to look up the small and ever-changing details for builtins like this from time to time, or at least check the book's surrounding context for more clarity.

Still, the edit will help stave off confusion for 3.X users looking for stricter definitions than tutorials are generally designed to provide. For tighter coverage, my book "Python Pocket Reference" provides the more complete and accurate description of round() that references should.

Anonymous  Dec 17, 2020  Jul 02, 2021
PDF
Page 158
2nd paragraph

You say

The following in 3.3 gives the same result as the previous output:

>>> print(0.1 + 0.1 + 0.1 - 0.3) # Pythons < 2.7, 3.1
5.55111512313e-17

The version number(s) in the comment and the prose don't match. I think you meant to say "The following in versions prior to 3.1 and 2.7 gives the same result as the previous output".

Another problem with this is that you say the two examples give the same result. But, they aren't the same. The second is a rounded version of the first.

Also, once again you say "On Python ..." in this paragraph instead of "In Python ..."

Note from the Author or Editor:
This narrative tries to accommodate an odd and trivial print precision change present as of 3.3, but it's really not germane to the topic here in any event. So, let's change this way:

1) Page 158, start of paragraph #2, change the first to the second:
"On Pythons prior to 3.1 and 2.7, printing ..."
"Printing ..."

2) Page 158, comment on right of code line #1 of 2nd code listing, change first to second:
"# Pythons < 2.7, 3.1"
"# Earlier Pythons (3.3 differs)"

[Discussion only follows]
The 'other problem' isn't present. The sentence preceding the code states: "The following in 3.3 gives the same result as the previous output:", which means it differs from the next line. The results do differ slightly in 2.7, but that's not what was stated here.

The 'on/in' point goes away here with this edit, but does not require universal consistency in general. This book's narrative is informal.

Jon Forrest  Oct 05, 2014  Oct 29, 2014
Printed
Page 159
1st new paragraph (starting "This") and example following

The paragraph talks about monetary applications and representing cents with two decimal digits. The example suggests that such "cents" in a monetary application will be handled properly if you set getcontext().prec to 2. But the Decimal "prec" (at least with an otherwise default context) is the number of total digits of precision in operations, not in the creation of a Decimal (which stores everything you feed it--see link below). So in the example, Decimal(str(1999 + 1.33)) creates a Decimal('2000.33'). But that actually has 6 digits of precision. Decimal('1999') + Decimal('1.33') in that same context actually yields Decimal('2.0E+3'), not Decimal('2000.33') as the example implies.

I assume there is a way to handle monetary applications (i.e. with a fixed number of "places" after the decimal point) with Decimals, but I don't know it (I am just learning Python ... that's why I'm reading the book). But it can't be by using getcontext().prec = 2 with an otherwise default context (I am using Python 3.3.2 on Win32, FWIW), as in the example.

See https://docs.python.org/3/library/decimal.html#decimal.FloatOperation
"The significance of a new Decimal is determined solely by the number of digits input. Context precision and rounding only come into play during arithmetic operations."

Note from the Author or Editor:
This post seems to expect too much of the deliberately limited Decimal coverage here (see below). To avoid similar confusion, though, let's change the potentially misleading paragraph in question from the first of the following to the second:

"This is especially useful for monetary applications, where cents are represented as two decimal digits. Decimals are essentially an alternative to manual rounding and string formatting in this context:"

"Technically, significance is determined by digits input, and precision is applied on math operations. Although more subtle than we can explore in this brief overview, this property can make decimals useful as the basis for some monetary applications, and may sometimes serve as an alternative to manual rounding and string formatting:"

[Discussion only follows]
All true, and thanks for the informative post which others may find helpful. But you seem to be reading too much into the book's very limited and even terse coverage of Decimals. This is a 2-page tutorial in a chapter on number basics, about a complex tool that has a limited audience, and changes often across Python releases. Its code samples are not the whole story, and weren't meant to be.

The book is at best an introduction to this obscure topic -- as it is for Fractions in the same section, complex numbers earlier in the chapter, and namedtuples in later chapters. Its examples do work as shown: the code in question relies on the 2-digit str() result to set initial sizes, but the "prec" setting would also be applied on future math operations. Still, Decimal is much more complex than presented in the book. The book expects readers interested in the subject to go the docs for more usage details, which you clearly have.

Or, to quote from this brief section's conclusion in the book:
"""
Because use of the decimal type is still relatively rare in practice, I’ll defer to Python’s standard library manuals and interactive help for more details.
"""
Given that, I'm disinclined to write much more about them either in the book or here, when the standard manual fills in their background and details well. Hence, no book changes required apart from the minor rewording above, but post retained for other readers, and category changed from "serious technical mistake" to "clarification", as the text seems to have worked exactly as designed on this.

That said, and viewing this post as a request for clarification, the Decimal class can form the _basis_ of monetary calculations, but it takes more to flesh out a full implementation. For pointers, try a web search for Python Money classes (that yields the following), and see the FAQ on the quantize() method in Decimal's standard library manual (the last of the following):
https://code.google.com/p/python-money/
https://pypi.python.org/pypi/py-moneyed
https://docs.python.org/3/library/decimal.html

Python isn't COBOL, and floating-point and integer suffice as monetary data for many; but fixed point solutions are also readily available. Decimal itself is complex and nonintuitive, is largely skipped in the text, and requires additional research on the part of readers.

Frank Hollander  Oct 19, 2014  Oct 29, 2014
Printed
Page 165
After 3rd paragraph

In the line
>>> for item in set('abc'): print(item * 3)
the round brackets after the print statement should be deleted
because the section is headed "Set basics in Python 2.6 and earlier".

Note from the Author or Editor:
[Not an error, no fix required, type changed to clarification]

I'm going to dismiss this, but keep it as a clarification for other readers. Because the code in question works correctly in Python 2.X, this seems a stylistic preference. As described later in the book, a "print(X)" works the same as a "print X" on Python 2.X, and even provides a portability path to 3.X -- the extraneous parentheses are simply ignored in 2.X as enclosing a one-item expression, and are not printed. The parentheses would be printed for zero or multiple items, as they are then taken to form a tuple -- requiring other techniques outlined in the book. See page 367 in particular for "print(X)" examples in 2.X. In hindsight, this might have been footnoted earlier in the text, but there's no space for an insert.

Eckhard  Feb 28, 2016  Jan 06, 2017
Printed
Page 169
1st Paragraph under Sets - "...an unordered collection of unique and immutable objects..."

Beginning on page 169 and continuing through page 173 sets are described as being immutable.

However, from the language reference (3.7.9) https://docs.python.org/3.7/library/stdtypes.html#set-types-set-frozenset

under set types-

"There are currently two built-in set types, set and frozenset. The set type is mutable — the contents can be changed using methods like add() and remove(). Since it is mutable, it has no hash value and cannot be used as either a dictionary key or as an element of another set. The frozenset type is immutable and hashable — its contents cannot be altered after it is created; it can therefore be used as a dictionary key or as an element of another set."

This seems to contradict what you have written. Can you help me understand? I am new to this so if I have been inappropriate, I do apologize

Thank you
Terry Kummell

Note from the Author or Editor:
[No changes required, retained as a note] Thanks for your input, but no: the book does not describe sets as immutable anywhere. Instead, it correctly states that sets' _items_ must be immutable, even though sets are mutable.

This distinction may seem subtle, but it's a core requirement of the set type.
You can change the set itself in place with methods like ".add()" and operators like "|=" (hence sets are not immutable). But you can store only unchangeable items within the set (hence the items nested within the set are immutable).

This follows from the fact that set items, like dictionary keys, must be hashable: an object whose value may change cannot generally produce a unique and unchanging hash value. Thus, you can store immutable numbers, strings, and tuples in a set -- but not mutable lists:

>>> x = set()
>>> x |= set([123, 'spam', (1.0, 2.0)])
>>> x
{(1.0, 2.0), 123, 'spam'}
>>> x |= set([[4, 5, 6]])
TypeError: unhashable type: 'list'

So, sets themselves are mutable, but their items are not. This is essentially the inverse of tuples -- tuples themselves are immutable, but the items nested within them need not be. That is, sets are mutable collections of immutable objects, and tuples are immutable collections of immutable or mutable objects (tongue twister but true):

>>> t = (1, 'spam', [4, 5, 6])
>>> t[2][0] = 99
>>> t
(1, 'spam', [99, 5, 6])
>>> t[2] ==99
False

The set's constraint on nested items is fairly clear in the book's opening definition: "...the set—an unordered collection of unique and immutable objects...". This is also covered with examples on page 173, where the book is careful to call out the "frozenset" immutable variant type too. Sets are also listed as mutable on page 244, and 277 restates that "set items [edit: not sets!] are unordered, unique, and immutable".

I understand the confusion, but please reread the book's sections on sets with this in mind, and follow up if needed; as described by both the book and Python's docs, immutability applies to sets' items, not sets.

Terry Kummell  Jan 14, 2021  Jul 02, 2021
PDF
Page 177
Fig 6.1

Figure 6.1 and its caption use the word "names" in several places. It would be clearer, and more consistent, if you instead used "variables".

Note from the Author or Editor:
The equivalence is already mentioned once on the prior page (see below); but to further clarify, let's apply two edit in reprints.

1) On Page 176, start of paragraph 2, change the first of the following to the second, with its "name" in italics for emphasis:
"A variable (i.e., name), like a,"
"A variable (also known in Python as a name), like a, ..."

2) On Page 177, start of caption to Figure 6-1, change the first of the following to the second:
"Figure 6-1. Names and objects ..."
"Figure 6-1. Names (a.k.a. variables) and objects ..."

[Discussion only follows]
I agree with the poster in principle, but the book points out the equivalence of "variable" and "name" on Page 176, just before the caption mentioned. It reads like this (with an "i.e." for "that is"):
"""
Variable creation
A variable (i.e., name), like a, is created when your code...
"""
Still, that's a bit terse, and the edits above may help make this more obvious and avoid confusion. Using one or the other terms universally isn't an option at this point, given their widespread usage.

The occasional "name" also highlights distinctions with other languages, and you'll generally see "name" in error messages from Python itself, despite the common use of "variable" in math. Using both terms is intended to have wider scope. In any event, "variables" would be the worse of the two to use -- it's "name" almost everywhere in Python.

Jon Forrest  Oct 05, 2014  Oct 29, 2014
Printed, PDF, ePub
Page 179
2nd paragraph in the box "More on Python Garbage Collection"

"exercise 3" should be "exercise 6"

Note from the Author or Editor:
Yes, a valid typo (the exercise likely moved over time). In the referenced paragraph, please change:
"For example, exercise 3 at the end of Part I and"
to:
"For example, exercise 6 at the end of Part I and"

Anonymous  Jun 01, 2015  Jun 26, 2015
Printed, PDF, ePub, Mobi,
Page 192
3rd line of text, below table

Minor, but the wrong chapter is referenced in one place here (it's given correctly as 37 elsewhere, often, and nearby).

CHANGE:
"module, introduced in Chapter 4 and Chapter 36,"
to this, replacing 36 with 37 and adjusting the link:
"module, introduced in Chapter 4 and Chapter 37,"

(Also note that, as stated in the book, the "re" module shows up in brief examples in Chapters 4 and 37, but is not covered by this book in any sort of useable depth -- on purpose. Instead, Programming Python has an entire dedicated chapter on text processing that delves into pattern matching in a much more useful fashion, and Python Pocket Reference gives complete reference details for "re".)

Mark Lutz
 
Apr 28, 2014  Aug 22, 2014
PDF
Page 196
start of paragraph 6

(This is on page 190 in printings before Sep-16.)

Please change the following arguably-ambiguous sentence:
"Again, though, because most programmers don’t need to come to grips with Unicode details up front, I’ve moved most of them to Chapter 37."

To this:
"Again, though, because most programmers don’t need to come to grips with Unicode details up front, I’ve moved most of the details to Chapter 37."

In truth, there are no programmers imprisoned in Chapter 37....

Mark Lutz
 
Sep 07, 2016  Jan 06, 2017
Printed
Page 200
1st row of last paragraph

As shown in the last row in Table 7-1, you can also iterate over strings...

should be:

As shown in the second to last row in Table 7-1, you can also iterate over strings...

because the last row in Table 7-1 is about pattern matching and the second to last row is about iteration and membership.

Note from the Author or Editor:
[On page 207 para 4 in printings on or after Sep-2016]
Agreed this should be changed (the pattern-matching line was a late addition), but "second to last" doesn't seem quite accurate, as there are multiple entries in the category. Instead, let's change the first of the following to the second:"
"As shown in the last row in Table 7-1,"
"As shown near the end of Table 7-1,"

Laszlo Nagy  Sep 26, 2016  Jan 06, 2017
Page 202
last line of example code

the last line of code should not contain a SPACE between "a" and "m".

It should be like:
">>> print(s)
s p
am"

NOT the one on the book:
>>> print(s)
s p
a m

Note from the Author or Editor:
[No edits required] No, the example in the book is correct as shown, but its output may vary across platforms and tools. This code is printing the string:

>>> S = "s\tp\na\x00m"

which contains an embedded tab (\t) after the 's,' and a null character (\x00) after the 'a.' Per Unix convention, a tab counts for as many blank spaces as required to move the output column to the next multiple of 8, which is why the 'p' is spaced further to the right than the 'm' in the book.

The null character's effect, however, seems more open to interpretation: it renders one blank space on Windows Command Prompt, but as nothing in macOS Terminal. Since this example was run on Windows for the book, it's output reflects that platform's results:

>>> print(S)
s p
a m

On macOS and possibly others, the last line is indeed just "am" as you noted, but that wasn't the book's code host (as noted in its Preface), and this is apt to vary arbitrarily. So, what your seeing may differ form the book slightly, but this seems a minor interoperability issue that may be better ignored than covered so early in the text. We will, however, keep this note for the next reader who may be tripped up by this.

Xianle Yan  Aug 01, 2021  Jul 15, 2022
Printed, PDF, ePub, Mobi,
Page 213
paragraph -2, line 3, clause following the dash

(This is on page 206 in earlier printings). A reader wrote by email to ask about the use of "binary" in this clause. Since this is a common confusion (and was addressed elsewhere in the book by previous errata), let's change this clause from:
this returns the actual binary value used to represent
to:
this returns the actual numeric value used to represent

Discussion: this isn't an error in the book, but readers seem to frequently confuse in-memory storage with human-readable display. The use of "binary" at some places in the book refers to the former, not the latter. To help clarify the important distinction, I've posted a fuller look at the issue at this page:

https://www.learning-python.com/book-queries-lp5e.html#q_2

The edit above should minimize some confusion, but I hope the post goes further to clear up some common misunderstandings of the fundamentals to which the text was referring.

Mark Lutz
 
Nov 17, 2019  Nov 22, 2019
Printed, PDF, ePub, Mobi,
Page 223
"Adding Keys, Attributes, and Offsets" section

The book says: '[...] The first of the following examples indexes a dictionary on the key "spam" and then fetches....'

I think it should be: ''[...] The first of the following examples indexes a dictionary on the key "KIND" and then fetches...." because according to the example:

>>> 'My {1[kind]} runs {0.platform}'.format(sys, {'kind': 'laptop'})

Note from the Author or Editor:
Yes, a valid typo. Please replace "spam" with "kind" (lowercase) in the sentence described. This is in line 5 of the last paragraph on page 223. Inherited from the book's prior edition, where the code actually did use the less mnemonic key "spam", but was edited for space.

Antonio Cota  Jul 15, 2014  Aug 22, 2014
, Printed, PDF, ePub, Mobi, , Other Digital Version
Page 247
first paragraph of note box at end of page

Minor nit, but it would be useful to add a back-reference to where this topic was first noted. In line 2 of this note, just before the emdash designated with a double dash here, change: "and a list) work--the"
to: "and a list) work as first noted in Chapter 5--the".

Mark Lutz
 
Jul 11, 2013  Aug 16, 2013
Printed, PDF, ePub
Page 252
table 8-2, line -6, column 2

A reader sent this by direct email. Two readers have now logged confusion on this; while it's correct as is, it's worth a minor rewording to avoid further issues.

In the table's entry for Operation
D[key] = 42
change the Interpretation text in column 2 from:
Adding/changing keys
to:
Adding keys, changing key values

[Discussion] The original is correct, because this syntax is used both to insert new keys, and to change key's values (in the same way that a _variable_ is said to be changed by assigning it a new value--keys and variables are both assignment targets). I suspect some readers are reading this differently, though, assuming that keys can somehow themselves be changed in-place; really, we may add and remove keys, but only change them by making them reference new values. A minor shade of difference to be sure, but it's important to avoid confusion early in the book.

Mark Lutz
 
Mar 15, 2015  Mar 20, 2015
Printed, PDF, ePub, Mobi,
Page 253
bottom paragraph

The text (penultimate line) says "Technically, this ['in' for dictionaries] works because dictionaries define iterators that step through their keys lists automatically."

I was very surprised by this assertion and seriously doubted that it is true: because dictionaries are implemented as hashes, the operation of deciding whether or not a key is in the dictionary will be very fast by using the hashing algorithm, and should not be done via creating a list/iterable of the keys and linearly searching through it. And checking the Python 3.4 source code, I find that this is indeed the way it is implemented - the 'in' operation for dictionaries is special-cased in exactly this way. See Objects/dictobject.c, around line 2615:

/* Hack to implement "key in dict" */
static PySequenceMethods dict_as_sequence = {
0, /* sq_length */
0, /* sq_concat */
0, /* sq_repeat */
0, /* sq_item */
0, /* sq_slice */
0, /* sq_ass_item */
0, /* sq_ass_slice */
PyDict_Contains, /* sq_contains */
0, /* sq_inplace_concat */
0, /* sq_inplace_repeat */
};

and PyDict_Contains just checks whether the key hash is found in the dictionary without attempting to list all the keys.

A better replacement for this sentence would therefore be: "Although the 'in' membership test for iterables works by iterating through the iterms, dictionaries are treated as a special case because their keys can be looked up very quickly." Or maybe: "The 'in' membership test for dictionary keys is implemented using the fast key lookup algorithm, so it is very efficient." Or something like that.

Note from the Author or Editor:
This may be splitting hairs a bit (as explained below), but to avoid future confusion, let's change this sentence from:

"Technically, this works because dictionaries define iterators that step through their keys lists automatically."

to this:

"Technically, this works because dictionaries define keys iterators, and use fast direct lookups whenever possible."

[Discussion only follows]
I'm marking this as clarification, not errata. Your analysis is technically correct (and kudos on both your exploration and explanation), but the sentence in question was not meant to be rigorous, and was not intended to imply a specific implementation mechanism. Instead, this is a generic and intentionally simplistic statement at an early point in the book when most readers are probably not ready to grapple with subtle shades of difference between general __iter__ and specialized __contains__ methods.

Dictionaries' internal implementation by hash tables is stated elsewhere in this book (see Page 251's paragraph on the subject), and the __contains__ (i.e., "in" operator) customization for dictionaries is mentioned both in a footnote on Page 482, and in the full section on the subject on Pages 906-909. The text straddling Pages 906/907 especially seems to call this out, once OOP has become fair game in the book:

"""
In the iterations domain, classes can implement the in membership operator as an iteration, using either the __iter__ or __getitem__ methods. To support more specific membership, though, classes may code a __contains__ method ? when present, this method is preferred over __iter__, which is preferred over __getitem__. The __contains__ method should define membership as applying to keys for a mapping (and can use quick lookups), and as a search for sequences.
"""

The C code you referenced is the internal equivalent of these class methods. That said, a minor edit here may help disambiguate. I'd also note that discovering this by exploring Python's implementation itself is a great way to clarify such things in open source projects.

Julian Gilbey  Jun 07, 2014  Aug 22, 2014
Printed
Page 254
Fourth line of first full paragraph

Change "required some" to "required in some"

Note from the Author or Editor:
[On page 262 para -2 line 4 in printings on or after Sep-2016]
Yes - a genuine missing-word typo. Please change as suggested, inserting the "in".

Nick Reeder  Oct 28, 2016  Jan 06, 2017
PDF
Page 255
1st paragraph

"As for keys, these two methods also return iterable objects in 3.X, so wrapthem in a list call there to collect their values all at once for display:"

I am thinking the intent here was to say "As WITH keys,.........."

Note from the Author or Editor:
This seems to be picking nits, but the "for" could conceivably be misunderstood as referring to a Python for loop here. So, yes: please change the "As for keys," to "As with keys," retaining the literal font on "keys".

newagequanta  Feb 20, 2016  Sep 02, 2016
Printed, PDF, ePub, Mobi,
Page 261
Very end of last paragraph on this page

In response to a post in a reader forum, I'm going to add some parenthetical text for clarity. Change the end of this paragraph from:

"plays the role of top-level container in realistic programs:"

to:

"plays the role of top-level container in realistic programs (the following snippets both print Bob's 2-item job list if run live and provided with another record structure):"

For the rationale behind this, see the original post and reply at my reader FAQ page, https://www.learning-python.com/book-queries-lp5e.html#q3.

Mark Lutz
 
Oct 22, 2013  Nov 08, 2013
Printed
Page 264
Second-to-last line

Delete ")" at the end of "D.viewitems)"

Note from the Author or Editor:
[On page 274 line 5 in printings on or after Sep-2016]
Yes, there's a stray ")" here for reasons lost to the ages, which has successfully eluded the best intentions of both human and mechanical devices (we probably nuked a matching "(" somewhere along the way). Please change the ", D.viewitems);" to the more correct ", and D.viewitems;", with normal font on "and".

Nick Reeder  Oct 28, 2016  Jan 06, 2017
PDF, Mobi
Page 265
4th paragraph, starts with "In Python 3.X and 2.7..."

End of paragraph reads:
"(it reads almost the same in Python, but with a bit more formality):"

After the word "Python," there should be a version number.

Note from the Author or Editor:
This is at the bottom of page 274 in printings after Sep-2017. It's not an errata and the suggested change is invalid, but to avoid potential confusion, let's change the text:
"(it reads almost the same in Python,"
to:
"(the Python code reads almost the same as its natural-language description,"
It looks like the expansion won't change paging, but please let me know if it does.

[Discussion only follows] This is not an errata, but I think the intent of the parenthesized remark was missed and merits the elaboration. It wasn't about Python 2.X/3.X differences, but only a note that the Python code is very close to its preceding English description. In fact, this code runs the same on 2.X and 3.X, so version is irrelevant here (as is strongly implied by the "In Python 3.X and 2.7," at the start of this paragraph):

~$ python
Python 2.7.10 (default, Oct 23 2015, 19:19:21)
>>> D = {k: v for (k, v) in zip(['a', 'b', 'c'], [1, 2, 3])}
>>> D
{'a': 1, 'c': 3, 'b': 2}

~$ python3
Python 3.5.2 (v3.5.2:4def2a2901a5, Jun 26 2016, 10:47:25)
>>> D = {k: v for (k, v) in zip(['a', 'b', 'c'], [1, 2, 3])}
>>> D
{'a': 1, 'c': 3, 'b': 2}

The ordering of the result is prone to vary per run, version, and platform (per the notebox a few pages earlier, titled "Your dictionary ordering may vary"), but that wasn't the point of the parenthetical remark. Post type changed to clarification, accordingly.

Lee Creighton  May 22, 2017  Jul 14, 2017
Printed
Page 266
Last paragraph, first sentence

"which simply means objects that generate result items one at a time"

should read

"which simply means that objects generate result items one at a time"

("objects that" --> "that objects")

Note from the Author or Editor:
[No changes required] No, this clause is meant to define the italicized "iterables" that immediately precedes it. The "objects" corresponds to the "iterables" directly:

View objects are _iterables_, which simply means objects that generate...

Please read the entire sentence in this light: if it still seems off, feel free to follow up.

Anonymous  Apr 01, 2021  Jul 02, 2021
Page 266
Last paragraph, first sentence

Following up on the discussion for:

"which simply means objects that generate result items one at a time"

I now understand your phrasing. It is admittedly minor, though I do think the syntax is slightly ambiguous, and it did trip me up when reading. Obviously it’s not worth tussling over, though I might recommend any of these changes, if you like:

"View objects are *iterables*—objects that generate result items one at a time..."

"View objects are *iterables*, that is, objects that generate result items one at a time..."

"View objects are *iterables*, i.e., objects that generate result items one at a time..."

"View objects are *iterables*, which simply means they are objects that generate result items one at a time..."

(This is near the top of p.276 in newer editions)

Note from the Author or Editor:
[No edits required] Thanks for your follow-up. All of your alternative wordings are reasonable, and I'll leave this for other readers to draw their own conclusions. As an author, I'd only add that if every sentence in this book was so thoroughly scrutinized, there probably wouldn't be a book at all.

Anonymous  Sep 09, 2021  Jul 15, 2022
Printed, PDF, ePub
Page 268
lines 7 and 11 of first code listing

Line 7: "['b', 'c', 'a']" appears to have one leading space at its start; the leading space should be removed.

Line 11: ">>> D" is all italics; all italics on this line should be removed (and "D" should be bold, per interactive input formatting--like line 2).

Mark Lutz
 
Oct 15, 2014  Oct 29, 2014
Page 270
2nd Paragraph titled "Dictionary magnitude comparisons no longer work in 3.X"

It is said that: "However, you can simulate it by comparing sorted keys lists manually: `sorted(D1.items()) < sorted(D2.items())`" . Which should instead be: "sorted(D1.keys()) < sorted(D2.keys())". Note the `keys()` method used in the above modified snippet. In the original snippet `items()` methods was used while simulating comparing sorted keys lists. That is, using `keys()` method suits the description/explanation given in the corresponding paragraph.

Note from the Author or Editor:
[Edit] This is on page 279 in later printings. Please change:
"sorted keys lists manually:" to
"sorted key/value pairs manually:".

[Discussion] This post is correct that the code's use of items() is a bit askew from the narrative preceding it, but we must change the _narrative_, not the code. Dictionary magnitude comparisons must use items(), not keys(), else the values of the same key in the operands will not factor into the result. Comparing dict(A=1) to dict(A=2), for example, must compare the values 1 and 2 of the matching keys A; items() includes these values, but keys() gives just the two keys A.

It's worth noting that this is also described more accurately and in much more detail in the next chapter's coverage, which is referenced on this page. When in doubt, see ahead.

Anoop Rana  Mar 31, 2022  Jul 15, 2022
Printed, PDF, ePub, Mobi, , Other Digital Version
Page 287
Last sentence of Windows note

"second" should be "first". (The first of the three command examples uses a raw string.)

Note from the Author or Editor:
Yes--a valid typo, in a new note box about Windows fie paths added in this edition. As suggested, change:
"The raw string form in the second command..."
to:
"The raw string form in the first command..."

Christopher Karl Johansen  Aug 27, 2013  Nov 08, 2013
Other Digital Version
290-291
2nd paragraph

Version: Digital Google Play Book.
The Answer #6, to quiz of Chapter 7, explains that the total character count is:

"SIX. The string 'a\nb\x1f\000d' contains the characters a , newline ( \n ), literal value 31 (a hex escape \x1f ), literal value 0 (an octal escape \000 ), and d . Pass the string to the built-in len function to verify this, and print each of its character’s ord results to see the actual code point (identifying number) values. See Table 7-2 for more details on escapes."

However, although the total characters are 6 indeed, as the len suggested function checks Ok, in the detail character list explain below seems to be missing the character "b".

As a way to check this fact I've tried the instruction:
>>>str(len("a"))+'-'+str(len("\n"))+'-'+str(len("b"))+'-'+str(len("\x1f"))+'-'+str(len("\000"))+'-'+str(len("d"))
and the output is:
'1-1-1-1-1-1'

Thanks for your answer with an explanation.

Carlos

Note from the Author or Editor:
REPRINTS: this is in the solution to exercise 6 on page 246 of recent printings (and 238 earlier). It looks like this broke in an earlier reprint. To fix, change the text at the end of the first line of solution 6 from:
newline (\n), literal value
to the following, putting back the former literal-font "b" which was cut:
newline (\n), b, literal value
This shouldn't change page breaks.

DISCUSSION: you're right - the description is missing the "b." In this case, the "b" was there in the original published book, but was inadvertently removed when an unrelated edit was applied to this sentence for a prior reprint.

This apparently broke some time around October 2016, but went unnoticed until your report. For context, see the 'before' screenshot of the book's earlier PDF which had the "b," and the current 'after' PDF which does not:

learning-python.com/lp5e-page246-before.png
learning-python.com/lp5e-page246-after.png

This is not about finding fault, and glitches happen, of course (no, really: this round of updates has also uncovered bugs in Mac OS and a program example, plus the usual set of silly typos that went unnoticed for seven years). The good news is that, thanks to your report, we'll be able to put back the cut "b" in the next reprint, set for early June 2020.

Carlos Sims  Feb 19, 2020  Jun 05, 2020
Printed
Page 303
Second-to-last line before last code listing

Change "dictionary methods" to "dictionary method"

Note from the Author or Editor:
[On page 313 para 3 in printings on or after Sep-2016]
Yes - change "the items dictionary methods" to "the items dictionary method".

Nick Reeder  Oct 28, 2016  Jan 06, 2017
Printed, PDF
Page 306
4th paragraph, 2nd sentence (3rd line)

A reader sent this by direct email. At the end of this line, change:
In Python 3.X names all references classes,
to:
In Python 3.X these names reference classes,
Hopefully this doesn't impact page breaks (let me know if so).

Mark Lutz
 
Mar 15, 2015  Mar 20, 2015
Printed
Page 324
just before the middle; at end of the intro paragraph for exercise 2

"slicing out of bounds can help, for example, if a sequence is as long as you expect" would be more helpful, I think, if you inserted the word "not" before "as long".

Note from the Author or Editor:
Yes, and good catch (though this is more simple typo than technical mistake). Please change the "is as long as you expect" in this exercise description to "is not as long as you expect".

Bill Evans  Nov 14, 2019  Nov 22, 2019
Printed, PDF, ePub, Mobi,
Page 333
End of first output listing

Add a new line to the end of the first output listing on this page that appears just before heading "Handling Errors with try Statements". This new final line should contain a single word, "Bye", non-bold and without quotes. The last 3 lines in this listing will be this (with only "stop" bold, and similar to other output listings nearby):

100
Enter text:stop
Bye

[Discussion] This was reported by a reader by direct email; my reply with description follows:

I appreciate your report on this. You are correct, of course: the code listing on page 332 has a final "Bye" print statement, whose output does not appear in the output listing following it on page 333. I'll add an errata note to patch this in reprints.

This isn't critical, as the "Bye" does appear in the first such example in this section on page 331-332, and again in some later variations' output listings on pages 335 and 336. It could be argued that it's not necessary to show the complete output at each such variation, especially after the first establishes the pattern. But I agree that it would be better to be consistent about this throughout the section.

For the record, the "Bye" output line was also missing in the 4th Edition for this same mid-section example, but went unreported; this might underscore its relatively low priority.

> -----Original Message-----
> im wondering about something in your book
> first of all see the "first" photo attached
> the last statement {print('Bye')} thats means when the while loop ends its must print 'Bye'
> But on the other hand .. check the 'second' photo attached
> at the end when the user typed stop the loop ends and 'bye' was missed
> now am i right ?!
> these at (Chapter 10: Introducing Python Statements)
> page 322
> thanks!

Mark Lutz
 
Jul 20, 2014  Aug 22, 2014
PDF
Page 340
Several places

You describe assignments in two ways:

1) "in the second line of Table 11-1, the name spam is assigned the string 'yum'"

2) "and the name ham is bound to the string 'YUM'"

This makes it sound like there's a difference between assigning a string and binding a string. I suggest you remove "is bound" in the second case.

Also, you say "any sequence of names can be assigned to any sequence of values". I think this is backwards. Shouldn't this be

"any sequence of values can be assigned to any sequence of names"

or maybe simply removing one word

"any sequence of names can be assigned any sequence of values"

Note from the Author or Editor:
I agree with one point in this post: the equivalence of "bind" and "assign" could be spelled out explicitly. To tighten up, near the end of the first complete bullet item on Page 340, change:
"""
all these contexts simply bind names to object references at runtime.
"""
to the following, using italics font for the "bind" (this is its first appearance):
"""
all these contexts simply bind (i.e., assign) names to object references at runtime.
"""

Both assign and bind are used interchangeably in the Python world, and the book reflects this vocabulary intentionally. The rest of this post, while appreciated, seems personal preference on minor wording alternatives.

Jon Forrest  Nov 28, 2014  Jan 09, 2014
Printed, PDF, ePub, Mobi, , Other Digital Version
Page 341
7th line on page

This was filed by a reader as an errata against the 4th edition, but I'm reposting it here against the 5th, which inherits the same text.

The patch: on Page 341, in line 7, change:
"...rest: a is assigned 's', and b is assigned 'pam'."
to:
"...rest: a is assigned 's', and b is assigned ['p', 'a', 'm']."

The issue: per the reader's report, the text states the former, but the latter is more accurate, While true, this is an introductory description only and is deliberately informal; moreover, the actual and formal output of this code is shown explicitly by example later in this section. To avoid confusion, though, let's make the patch as described.

For non-PDF e-book readers, this is in "Assignment Statement Forms" in Chapter 11, in list item "Extended sequence unpacking."

Mark Lutz
 
Jun 30, 2013  Aug 16, 2013
Printed
Page 341
Last sentence before section named Sequence Assignments

You state here and in the second-to-last paragraph on page 350 that there is an augmented assignment statement for every binary expression operator, but should "every" be "most" or "many"? Are there augmented assignment statements for the comparison operators such as > ?

Note from the Author or Editor:
[On pages 351 para -2 and page 361 para 2 in printings on or after Sep-2016]
This was meant informally, but for the sake of more literal readers, please change the first to the second in both of the following places:

1) Page 351/341 (reworded)
"There is one augmented assignment statement for every binary expression operator in Python."
"As we'll see, there is one augmented assignment statement for most binary expression operators in Python."

2) Page 361/350 (every=>most, operator=>operators):
"As shown in Table 11-2, there are analogous augmented assignment forms for every Python binary expression operator"
"As shown in Table 11-2, there are analogous augmented assignment forms for most Python binary expression operators"

Discussion: I'd use "mathematically-oriented binary expressions" to narrow the set, but that's not quite right, as some have multiple meanings (e.g., '+' for addition and concatenation), and all can be defined to do anything at all by classes. The second instance above is less confusion-prone, as it shows up just after a table of these operators.

Nick Reeder  Oct 28, 2016  Jan 06, 2017
PDF
Page 346
2nd paragraph

The 2nd paragraph says, "a sequence unpacking assignment always returns a list for multiple matched items, whereas slicing returns a sequence of the same type as the object sliced". But the list is always return even if it it matches zero or more items, not only multiple (>1). Please see Python 3.7 interactive cmd prompt below.
>>> s='SPAM'
>>> a,b,*c,d=s
>>> a,b,c,d
('S', 'P', ['A'], 'M')
>>> a,b,*c,d,e=s
>>> a,b,c,d,e
('S', 'P', [], 'A', 'M')

Note from the Author or Editor:
[This is in the last paragraph on page 356 in later printings.] Technically correct, but result size wasn't the point here. This sentence was calling out the difference in return types for unpacking assignments and slicing. As stated in the book, the former is always a list, but the latter is subject-type specific. The distinction seems an odd special case in the language.

That said, the word "multiple" used informally here may be apt to confuse as per this post (the number of results doesn't matter), so let's delete the single word "multiple", changing:
a list for multiple matched items
to:
a list for matched items
This still may not explicitly cover the case of an empty list for no matched items, but this sentence wasn't meant to be rigorous with respect to boundary cases. Also changed this from technical mistake to language change, given its nature.

HSB  Feb 16, 2019  Mar 22, 2019
Printed, PDF, ePub, Mobi,
Page 363
End of paragraph 3 under

Not an error, but for clarity, expand this sentence:
"""
In fact, if you enjoy working harder than you must, you can also code print operations this way:
"""

To the following, adding only the parenthesized text just before the colon, with links to the two referenced chapters -- and please let me know if this changes page breaks; I'm hoping to avoid that:
"""
In fact, if you enjoy working harder than you must, you can also code print operations this way (per Chapters 4 and 9, a 3.X-only return value is omitted here):
"""

[Discussion only follows] A reader wrote by email:
>
> This is on page 357 of the Ebook; page numbering doesn't seem
> to match the print edition. Looking at the last example before the
> "Manual stream redirection" subheading in chapter 11:
>
> >>> import sys
> >>> sys.stdout.write('hello world\n')
> hello world
>
> The system call of course returns a result, and when run
> interactively the result is also printed. As such, when running this
> example under Linux an additional line is added:
>
> >>> import sys
> >>> sys.stdout.write('hello world\n')
> hello world
> 12
>
> where the "hello world" is the text written and the "12" is the result
> of the sys.stdout.write() call. I couldn't find any mention of this on
> your or O'Reilly's errata pages. The omission could be deliberate,
> but some people would find the extra line confusing. I would
> expect the Windows version would add a similar line. There is
> also no mention of this in the following text (i.e. that print always
> returns None where write() returns the number of characters
> written).

Sure, but this isn't platform specific, and is probably not an issue for most readers. The return value of the file's write method is well documented in general file coverage earlier in the book -- starting on Page 122, and spanning Chapters 4 and 9, including the blanket note on Page 288 that reads this way:

"... (for space, I'm omitting byte-count return values from write methods from here on):".

Still, this seems a potential point of confusion, especially for non-linear readers using 3.X. To clarify, I'm suggesting the addition of another parenthetical note about the deliberate omission of the return value in this section, as described above. Such reminders risk becoming overly redundant, but this one seems justified.

Mark Lutz
 
Oct 07, 2013  Nov 08, 2013
Printed
Page 364
Second-to-last paragraph

"then" in

"In other roles, streams may be reset to objects that display them in pop-up windows in GUIs, colorize then in IDEs like IDLE, and so on."

should be "them":

"In other roles, streams may be reset to objects that display them in pop-up windows in GUIs, colorize them in IDEs like IDLE, and so on."

Note from the Author or Editor:
Yes -- a valid typo; please repair as suggested.

Mathias Müller  Aug 24, 2015  Sep 04, 2015
Printed
Page 379
Last paragraph

After the last sentence on this page, there is a superscript "1" to refer to a footnote. However, the footnote itself is located at the bottom of the next page, 380.

(I am not entirely sure this is an error - please remove this erratum if it is not.)

Note from the Author or Editor:
Confirming this; it's a bit confusing as is, but it's fairly minor, and we'll have to see how it impacts paging if the footnote is moved to the referencing page.

Mathias Müller  Aug 24, 2015  Sep 02, 2016
Printed, PDF, ePub, Mobi,
Page 380
A Few Special Cases section (last paragraph)

the sentence 'indent all but the simplest of blocks', what does the book mean with "the simplest of blocks"? Which are the "simplest of blocks" it refers to?

Note from the Author or Editor:
[No changes required] The phrase "simplest of blocks" refers to a nested block that's composed of one or two simple, non-compound statements that are not indented, but coded on the same line as the statement header. It's generally consider bad style to do this when the unindented block's statements are non-trivial, as it can lead to code that's difficult to read and change.

Retained as a clarification, but no change required in the book. The page 380 text is an intentionally brief summary wrap-up of the second section that discusses nested block syntax, exceptions, and style. By this point in the book, both the meaning and disfavor of coding all but the "simplest of blocks" unindented have been discussed.

For example, the section on Page 329, and the multiple-page example which follows it, discuss and illustrate both this topic and the advice. I appreciate the question, and readers who jump to Page 380's wrap-up at random might indeed find it terse; but some extra research can help clarify topics covered in more depth earlier in the book.

Antonio Cota  Aug 01, 2014 
Printed
Page 383
4th paragraph

If that is the case, the effect is the same: the and runs first and returns Y if X is true; if X if false the and skips Y, and the or simply returns Z.

should be:

If that is the case, the effect is the same: the and runs first and returns Y if X is true; if X is false the and skips Y, and the or simply returns Z.

Note from the Author or Editor:
Yes (but difficult to notice even when you try!). At the start of line 3 in the referenced paragraph, change "; if X if false" to "; if X is false", replacing the second "if" with "is".

Laszlo Nagy  Oct 14, 2015  Nov 25, 2015
Printed
Page 389
3rd main paragraph

Another word break issue:

con - tinue broken across 2 lines.

There is another one on page 391, 2nd main paragraph.

Note from the Author or Editor:
Minor and cosmetic, but yes -- both of these should be fixed in the print version if possible. Do not split the "continue" keyword literal; force its "con" to the start of the next line, and verify that other literals in these paragraphs are not split in the process.

See also notes on pages 98 and 102 for similar items and more background. There are almost certainly more of these on later pages, but it's been a longstanding O'Reilly style, whether explicit policy or tool artifact. Given that this hasn't drawn more reader posts, it doesn't seem worth an exhaustive scan at this time.

Mitch Butler  Aug 18, 2014  Aug 22, 2014
Printed, PDF, ePub, Mobi,
Page 389
last example on page (under General Loop Format)

Change the comment on the right side of code line 4 from:
# Go to top of loop now, to test1

to this, retaining the original vertical alignment of the '#":
# Go to test at top of loop now

[Discussion] Just comment text and minor, and probably accepted by most readers because this did refer to the 1st of 3 tests. Still, "test1" sounds formal enough to be confusing. It's legacy from a 4th edition version which had a "test1", "test2", and "test3", but which was odd enough to merit a rework in the 5th edition.

The following was the original report submitted by Roger A. Davidson, moved here verbatim as this pertains to the 5th Edition:
"""
The second comment (explaining the continue statement) refers to going "to test1", but all the tests in the example are labeled simply "test" and there is no "test1." This is likely just a typo where the three tests were intended to be numbered "test1" for the while, "test2" for the break and "test3" for the continue.

It's a wonderfully detailed book though, and I'm learning a lot in my self-paced journey through it.
"""

Mark Lutz
 
Aug 20, 2014  Aug 22, 2014
Printed
Page 403
First paragraph, third word

The third word of the first paragraph, "range", should be formatted like all other Python keywords (constant width font).

Note from the Author or Editor:
Yes (minor but true); please make the referenced word literal font in reprints.

Mathias Müller  Aug 26, 2015  Sep 04, 2015
Printed
Page 411
Last line

The dot between os and popen is missing:

the command as a string to os popen, and read text...

should be:

the command as a string to os.popen, and read text...

Note from the Author or Editor:
Yes--a valid typo. As described, change "os popen" to "os.popen". This is the form used later in this section.

Laszlo Nagy  Oct 17, 2015  Nov 25, 2015
Printed, PDF, ePub, Mobi,
Page 413
Why You Will Care: Shell Commands and More (last paragraph - last line)

it says: "[...] but uses urllib instead of urlib.quest, ...."

I think it should be:

"[...] but uses urllib instead of urllib.quest, ...."

One "l" is missing in 'urlib.request'

Note from the Author or Editor:
Yes -- a valid typo and good catch, though the change should be from:
urlib.request
to:
urllib.request

Antonio Cota  Aug 03, 2014  Aug 22, 2014
Printed
Page 417
first bullet item

"The built-in range function (available since Python 0.X)"

The three-parameter range function is documented on python.org as early as version 1.4. I couldn't find any earlier versions to check. Recommendation: drop the parenthetical words.

Note from the Author or Editor:
[No changes required] Actually, the three-argument range() was present in Python 0.X -- you can find a Python 0.9 which leverages it in its library code at the following link (I used and remember 0.X too, of course, but I suspect you'll find the empirical evidence of a tarball to be more compelling):

https://www.python.org/download/releases/early/

All of which may seem just a historical anecdote. But in this case, the book's comparing four different looping techniques, and gives release versions of each to underscore how ingrained each has (or has not) been. Along with learner feedback, extra context like this is one of the unique things that this book brings to the table.

(Aside: I'm assuming that this post was meant as helpful rather than troll, but please be careful to avoid posting baseless opinions on this page. See its A WORD ON ETIQUETTE note for more details.)

Bill Evans  Nov 20, 2019 
PDF, ePub, Mobi,
Page 428
3rd text block

"[...] and the second strips whitespace on both ends to omit blank links [...]" should probably rather read "[...] to omit blank lines [...]".

Note from the Author or Editor:
Yes -- change just the italicized word "links" to "lines" in this text.
That is, it should read:

"..., and the second strips whitespace on both ends to omit blank lines in the tally..."

Minor typo, but the italics probably helped this one get past proofing.

Julian R.  Feb 20, 2014  May 02, 2014
Printed, PDF, ePub, Mobi,
Page 428
2nd code block

"[line.rstrip() for line in open('script2.py') if line.rstrip()[-1].isdigit()]"

The code raises an IndexError in Python 3.3.2 and 2.7.5.

Note from the Author or Editor:
[Not an errata, but retained as a clarification, with a minor supplemental insert]

CHANGE the last line of paragraph 2 on page 428 from:
"expression on the right side:"
to this, where [-1] and [-1:] are literal/fixed font:
"expression on the right side (replace [-1] with [-1:] for files with blank lines):"

DISCUSSION: This code works correctly and as shown when run on the 4-line test file whose content is given at the start of this section and implied by all the other examples here. See the first example on page 426:

>>> f = open('script2.py')
>>> lines = f.readlines()
>>> lines
['import sys\n', 'print(sys.path)\n', 'x = 2\n', 'print(x ** 32)\n']

If you use this same 4-line test file, the example in question works properly:

C:\code> type script2.py
import sys
print(sys.path)
x = 2
print(x ** 32)
C:\code> py -3
>>> [line.rstrip() for line in open('script2.py') if line.rstrip()[-1].isdigit()]
['x = 2']

That said, running this code on a file whose content is different than that shown in the book may indeed raise the exception, if the file used includes a blank line. This can occur because [-1] on an empty string triggers IndexError. Although this is a different context than that used in the book, the code could be easily generalized to allow for empty lines too, by slicing instead of indexing, like this:

>>> [line.rstrip() for line in open('script2.py') if line.rstrip()[-1:].isdigit()]
['x = 2']

This works more robustly, because, as explained in the book, slicing a string always returns a string, and slices scale themselves to be inbounds but indexing does not:

>>> 'abc1'[-1], 'abc1'[-1:]
('1', '1')
>>> ''[-1]
IndexError: string index out of range
>>> ''[-1:]
''

For background on such things, see the book's types part and exercises.

Anonymous  Apr 11, 2014  May 02, 2014
Printed, PDF, ePub, Mobi,
Page 431
Second paragraph

The first sentence begins:
"We first saw the sorted function used here at work in, and ..." - there's clearly a reference missing before the comma.

(Just commenting because I'm finding the book excellent, and am happy to help improve it.)

Note from the Author or Editor:
Yes -- we lost a link in this sentence. It should say this (add just the "Chapter 4" text/link):

"We first saw the sorted function used here at work in Chapter 4, and we used it for dictionaries in Chapter 8."

(Origin note: this is a minor issue, but it appears to have been broken during production. The "Chapter 4" appeared in both my final draft and the copyedit versions, but disappeared as of QC1.)

Julian Gilbey  Jun 07, 2014  Aug 22, 2014
Printed, PDF, ePub
Page 446
paragraph -2, line 4

Minor uncaught typo:
Change "Unix-stye #!" to "Unix-style #!" (adding just the "l").

Mark Lutz
 
Oct 15, 2014  Oct 29, 2014
Printed, PDF
Page 447
Code displayed in the interpreter

After "import docstrings", there is the output from the previous code snippet, "print(square(4))
print(square.__doc__)"

Note from the Author or Editor:
[No edits required - retained as info only] This example and its output is correct as is. The output listed after the ">>> import doscstrings" line is the intended result of the two print() lines at the bottom of the docstrings.py file.

The two print()s are top-level code, which means they will be run when the file is first imported as a module (just like when it's run as a script). That's just how Python modules work: the first import runs all the file's top-level code, which usually creates functions and classes to be used later, but may also trigger print() statements to generate output, as in this file.

That said, this example might confuse some readers because:

1) There is no output when the file is imported again later in the section (but also correctly: top-level code runs on first import only).

2) It may be a bit early in the text to assume module-file knowledge like this for random-access readers. Module basics like this are covered earlier in some depth in the section "Module Imports and Reloads" of Chapter 3, but not all readers work linearly.

Hence, I'm retaining this post as clarification for others. For more details on modules, see the Chapter 3 first look, as well as the fuller coverage in Part V.
The TOC and Index can also help if you're just flipping about the book.

Jonathan Herbert  Sep 09, 2018  Oct 12, 2018
PDF
Page 456
4th line from the last.

For instance, its string '#eeaa77' specifies 2-byte (16-bit) values

Should be:

For instance, its string '#eeaa77' specifies 3-byte (24-bit) values


Well, in old days, some PC uses 2-bytes to pack a pixel's RGB value with a coarse value for the Blue color. But nowadays, each RGB value has the same bit lengths.
There is no way to have equal number of bits for 3 values into a 16-bit entry. 16 is not divisible by 3.

Note from the Author or Editor:
(This is at the top of page 475 in printings after July, 2016.) Actually, the book's description here seems worse than this poster notes (and its origin is unclear). RGB strings are 24 bits, with 8 bits (1 byte) for each of the 3 values. They cannot be packed into 16 bits, as the poster explains, but that seems a minor graphics implementation detail; a fresh read of the original text seems to imply 2 bytes (16 bits) for each of the 3 values, which is clearly incorrect.

To fix, let's change the start of this sentence from:
"""
For instance, its string '#eeaa77' specifies 2-byte (16-bit) values for red, green, and blue levels
"""
to this:
"""
For instance, its string '#eeaa77' defines a 3-byte (24-bit) value, with 2 hexadecimal digits giving a 1-byte (8-bit) level value for each of red, green, and blue"
"""
If this causes a new page break, please let me know and I'll shorten the rewrite.

Jinsoo Shin  Jun 01, 2016  Sep 02, 2016
PDF
Page 459
In the command line example.

c:\code> set PYTHONPATH=.;%PYTYONPATH%

Should be:

c:\code> set PYTHONPATH=.;%PYTHONPATH%

This is a minor typing typo. Y is too near to the H in the QWERTY keyboard.
The author was working too hard to write this great book, and tired a lot when he write this part of the book.
Well, use the following for OS X or Linux platform:
export PYTHONPATH=.:${PYTHONPATH}

Note from the Author or Editor:
(This is at the bottom of page 477 in printings after July, 2016.) Yes, please change as prescribed: "PYTYONPATH" => "PYTHONPATH".

It's PYTHONPATH everywhere else in the book; the Unix export command note is well-taken; and yes, the author and his keyboard were both probably tired at this point in the project (as were all the copyeditors and proofreaders, apparently!).

Jinsoo Shin  Jun 01, 2016  Sep 02, 2016
Printed
Page 473
Table 16-1, line starting with def

the second column of this line says
def printer(messge):

I believe it should say:
def printer(message)

Note from the Author or Editor:
Yes. This is abstract sample code in a table that isn't actually run, so the variable name is unimportant; it should probably match that in the next line, though. Change as directed: "messge" => "message".

Alexander Hilsbos  Aug 22, 2014  Oct 29, 2014
Printed, PDF, ePub, Mobi, , Other Digital Version
Page 490
1st paragraph

"dictionary compressions". And in page 1109, "Unlike compression loop variables", I think they should be "comprehension".

Note from the Author or Editor:
Yes--change both of these: the 1st to "comprehensions" and the 2nd to "comprehension". The 1st occurrence is on Page 490, text line 6; the 2nd is Page 1109, paragraph 2, line 1.

Probably the result of the auto-correct-as-you-type feature in Word, and implied by surrounding text and context, but worth the patch.

Yang Lifu  Aug 15, 2013  Nov 08, 2013
Printed
Page 495
1st paragraph

The author used the word "sport" in various places. In the following sentence (on page 495) "As one consequence, a single function can
often be applied to a variety of object types—any objects that sport a compatible interface (methods and expressions) will do, regardless of their specific types."

Also on page 1131, second paragraph, "Moreover, 3.X sports slightly
modified syntax for the raise statement and except clauses, some of
which is available in 2.6 and 2.7."

On papge 1143, "Since an
else always requires an except, this nested form even sports the same mixing constraints of the unified statement form outlined in the preceding section."

I am wondering if the author intended to use the word "support" rather than "sport"?

I googled the English word "sport" and it does mean "wear or display (a distinctive or noticeable item)." Is it the meaning the author intended to use?

Note from the Author or Editor:
[No edits required] The use of "sport" in these contexts is intentional, and is indeed the last form you found -- to "wear or display (a distinctive or noticeable item)." In other words, it's a feature or attribute of the subject. That said, other readers have asked about this in the past too, so I'm retaining this note as a clarification here.

Wenjie Wang  Oct 29, 2018  Mar 22, 2019
Printed
Page 498
Third line of code listing

In the comment, change "make" to "makes" and change "explit" to "explicit"

Note from the Author or Editor:
[On page 519 line 3 in printings on or after Sep-2016]
Two typos in one comment - how do we live? To cure, please change the first of the following to the second, retaining the original spacing before the "#":
# Accessor make external changes explit
# Accessors make external changes explicit

Nick Reeder  Oct 29, 2016  Jan 06, 2017
Printed
Page 509
Last paragraph

A dot is missing:

References to enclosing defscopes work in 3X as

should be:

References to enclosing defscopes work in 3.X as

Note from the Author or Editor:
Yes--a valid typo. Please correct in reprints as described (adding a ".").

Laszlo Nagy  Nov 11, 2015  Nov 25, 2015
PDF
Page 515
last line

"though" should be "through"

Note from the Author or Editor:
Yes (another minor typo missed by many). Change the "state though the function’s name" to "state through the function’s name".

Anonymous  Jul 02, 2015  Sep 04, 2015
Printed
Page 517
Fourth line of Why You Will Care box

Insert a period after "page 494"

Note from the Author or Editor:
[On page 539 para 2 in printings on or after Sep-2016]
Yes - it looks like we dropped a period after the link here (probably an artifact of automatic formatting). Insert a period after the link's text that ends in "on page 514".

Nick Reeder  Oct 29, 2016  Jan 06, 2017
Printed, PDF, ePub, Mobi, , Other Digital Version
Page 518
Paragraph 1, line 2 (in sidebar)

Minor typo--delete extraneous "it", with this change:

"...nested def, after it saving the original..." =>
"...nested def, after saving the original..."

For e-book readers this is in sidebar "Why You Will Care: Customizing open" in Chapter 17.

Mark Lutz
 
Jun 30, 2013  Aug 16, 2013
Printed, PDF, ePub, Mobi,
Page 518
Code samples: definitions of makeopen (twice)

In both of the code samples, the positional arguments are defined and called as *kargs, while the keyword arguments are defined and called as **pargs. The choice of variable names is suggestive but the wrong way round for their respective meanings. I would suggest swapping the names and instead using *pargs and **kargs.

Note from the Author or Editor:
Yes: a minor typo and a bit tricky to adjust, but worth a patch. In the 1st code listing on this page, change this way, swapping "kargs" and "pargs" in all 3 of these lines (only), taking care to keep all else the same, including the "*" and "**" characters:

def custom(*pargs, **kargs):
print('Custom open call %r:' % id , pargs, kargs)
return original(*pargs, **kargs)

In the last code listing on this page, do likewise:

def __call__(self, *pargs, **kargs):
print('Custom open call %r:' % self.id, pargs, kargs)
return self.original(*pargs, **kargs)

This code works correctly as is and as shown, and the argument names are not the main point of this sidebar and its code -- nestable customization of a built-in with closures and classes.

But I agree that the two variable names are potentially a bit misleading, as "pargs" and "kargs" are used some 92 and 140 times elsewhere in the book, respectively, for positional and keyword arguments. Their swapped roles on this page most likely reflect a harried last-minute edit, followed by an unfortunate cut-and-paste.

Julian Gilbey  Jun 15, 2014  Aug 22, 2014
Page 519 under the section "Why You Will Care: Customizing open"
Top of the page

It is written that "where we'll find the closures are actually preferred".

There seems to be a typo in the above quoted sentence. In particular, word "the" should be "that". So after the correction the sentence should be:

"where we'll find that closures are actually preferred".

Note the replacement of "the" with "that" in the above modified statement.

Note from the Author or Editor:
Yes, please change "the closures" to "that closures" as this post suggests. This is at the end of the sidebar on page 540 in recent printings.

Anoop Rana  Jan 10, 2023 
Printed
Page 520
Last line of the page

it says: "[...] and the PRINT at the end finds the variable in the..."
but according the exercise number 5 there is no print statement, just `X`

Note from the Author or Editor:
Yes; this has been present since the 3rd edition so it's probably not too grievous (an interactive display is a print of sorts, though not a formal one), but it merits a patch. Change the middle of the last line on Page 52 from the first of these to the second, with no words in literal font:

"and the print at the end finds"
"and the display at the end finds"

Antonio Cota  Aug 23, 2014  Oct 29, 2014
PDF
Page 523
4th paragraph

[Assigning to argument names inside a function does not affect the caller.]
Argument names in the function header become new, local names when the function runs, in the scope of the function.

should be:
...Argument names in the function header become new, local names when the assignment is executed,...

Note from the Author or Editor:
[Not an errata, no change required]

I'm passing on this because the text is as it should be, but keeping the reply as a clarification-only note. This introductory preview paragraph makes the point that assignments to function argument names within a function's body have no effect on the caller. As described later in this chapter, argument names are automatically assigned to the values passed into the function, and are created when the function call begins. Unlike other locals, argument names do not become local variables when later assign inside the function; by that point, they already exist by virtue of the automatic assignment that occurs as arguments are passed in. This paragraph is about the fact that _later_ assignments to these names don't impact the caller -- as opposed to in-place changes to argument objects, the next paragraph's point. Subtle, perhaps, but explained in full later in this chapter; this text is just a preview.

Jach Fong  Oct 31, 2015  Jan 06, 2017
Printed, PDF, ePub, Mobi,
Page 524
Paragraph 2 in section "Arguments and Shared References"

[No change required: cross-posting a reply to a 4th Ed page 438 report, as the text in question is also on page 524 of the 5th Ed.]
A reader posted:

> Page: 438
> Location: 7th paragraph in page
> Description:
> "In this example the variable a is assigned with the object 88 at the
> moment the function is called with f(b), but a lives only within the
> called function."
>
> this should read...
> "In this example the variable a is assigned with the object 99 at the
> moment the function is called with f(a), but a lives only within the
> called function."
>
> changes:
> object 88 --> object 99
> f(b) --> f(a)

No, this wording is correct as is. The suggested change misses the whole point of this section, but seems a confusion common enough to warrant a clarification here.

This section describes the assignment of object 88 -- the value referenced by global variable b -- to variable a, which is a local variable within function f. This assignment occurs automatically when the function f is initially called.

That is, a is assigned 88 at the moment of function call, by virtue of the argument passing mechanism, as stated in the book. Names a and b share the same object at this point. The later assignment of name a to object 99 -- via the code a=99 within f -- simply resets local name a to a different object, and hence does not impact name b outside the function. That's the whole point of this example: the names a and b are not linked in any way.

That said, this text is describing a mechanism that has been historically confusing to some Python newcomers. When studying code like this, remember to keep the fundamental distinction of names and objects clear, and don't let prior language exposure cloud your judgement; Python is everywhere about names, objects, and references.

Mark Lutz
 
Oct 02, 2013 
Printed
Page 528
2nd paragraph

The text says, "This seems like an implementation artifact that is prone to change." Why would it be prone to change? People can trip over it if they're not paying attention, but without it, factory functions can't act as closures. Here's an artificial example:

#!/usr/bin/env python3

def makeMore():
def changer(y):
nonlocal i
i = y
return y
acts = []
for i in range(5):
acts.append(lambda x: i**x)
acts.append(lambda x: changer(x))
return acts

more = makeMore()
print(more[0](2))
print(more[1](2))
print(more[2](2))
print(more[4](2))
print(more[5](20))
print("not a bug, but a feature:")
print(more[0](2))
print(more[1](2))
print(more[2](2))
print(more[4](2))

If they change it, no more closure functionality in factory functions. That would break code (including mine) that takes advantage of this. Haven't they broken enough code already?

Note from the Author or Editor:
REPRINTS: Please change the referenced:
"This seems an implementation artifact that is prone to change, and may become"
to:
"This may seem an odd special case, but it reflects Python's implementation of variable scopes, and will become"
This is on page 507 in earlier reprints; it looks like this won't impact page breaks.

[DISCUSSION]. First off, please note that the text's statement you called out is not about closure state retention in general, but pertains only to passing default argument values to retain the current value of loop variables in an enclosing scope (this should be clear from its context). Your example doesn't do this; it uses nonlocal to modify an enclosing-scope variable manually, but it doesn't retain the value which the variable had at function-creation time.

In fact, the "i" in all the loop-generated functions in your code refers to the same, shared "i"; this is the exact limitation which the book's code works around. By ensuring that each function's "i" is unique, the book's code retains the current-loop value of each "i" in its own variable. There are lots of ways to code and use closures, and your example may have valid use cases; but it is not equivalent to that in the book, and misses its point. This is very subtle business, so please retrace the two examples for yourself.

That aside, the comment about defaults here reflects their history. As noted in the book, prior to nested scopes (and the later nonlocal which makes them changeable), closures in Python used default variables to explictly pass in each enclosing-scope value they used. This worked, but was odd and clumsy.

When enclosing scopes were added, they were meant to obviate this technique, but their implementation left holes that must still be plugged today; the default-arguments coding shown in the book's loop example is the standard work-around. This crops up frequently enough in GUIs to qualify as gotcha (and it does on page 683); seems like an implementation oversight that may be resolved in the future (e.g., by an option to save non-locals per function); and the rate of flux in Python 3.X makes change a not-bad bet.

Still, a change in closure semantics to address this use case seems less likely with every new Python release that ignores it. Because the odd default-arguments special case may now very well be permanent, I'm going to make the limited change above. But if this is tweaked in next years' 3.X release, I reserve the right to issue a hearty "I told you so"...

Bill Evans  Nov 28, 2019  Jun 05, 2020
Printed
Page 537
Next to the last line

(Book uses different font for function names. I replaced it with single quotes below.)
"This code relies on the fact that the function name 'nested' is a local variable in the 'tester' scope enclosing 'nested'; as such, it can be referenced freely inside 'nested'." <- The last word has to be 'tester'

Note from the Author or Editor:
[No changes required] I understand the confusion, but the 'nested' at the end of this sentence was intended and is correct. The 'nested' function relies on the fact that its own name is accessible to its code, because it is nested in 'tester', and nested functions automatically see the scopes of all enclosing functions by virtue of Python's normal LEGB variable-lookup rule.

This is underscored by the example's comment "# nested is in enclosing scope" in the 'nested' function. You're right that 'tester' can access 'nested' too (and does); but the point here was about the name's accessibility inside 'nested' due to the enclosing scope; when 'nested' later runs "nested.state += 1", it's relying on finding the leftmost name in 'tester' left by the function def, even though 'tester' has returned and exited. That is, this is a closure function which remembers 'nested' from the enclosing 'tester' scope.

This is quite subtle even with this clarification, so I'm retaining this as an author note for others puzzling over this example too.

Boris Pulatov  Nov 26, 2019 
Printed
Page 538
Fifth line before last code listing

In "Technically, it was added" the word "it" seems to refer to the unpacking call syntax form, but does it actually refer to the apply function?

Note from the Author or Editor:
[On page 559 para 2 in printings on or after Sep-2016]
The parenthesized sentence referred to is ambiguous as noted, but also confused in general, as its "it" spans two subjects (a last-minute patch, most likely). To fix it, please change the original:
"""
(Technically, it was added in 2.0, was documented as deprecated in 2.3, is still usable without warning in 2.7, and is gone in 3.0 and later.)
"""
to the following (and let me know if this changes the page-break here--I've reworded to try to avoid expansion):
"""
(Technically, the syntax was added in 2.0; the function was deemed deprecated in 2.3, is still usable without warning in 2.7, but is gone in 3.X.)
"""

Nick Reeder  Oct 29, 2016  Jan 06, 2017
Printed
Page 541
1st paragraph

for:

>>> def f(a, *b, c=6, **d): print(a, b, c, d)

the second test works in recent version of Python:

>>> f(1, *(2,3), **dict(x=4, y=5), c=7)
1 (2, 3) 7 {'y': 5, 'x': 4}

>>> sys.version
'3.5.0 (v3.5.0:374f501f4567, Sep 12 2015, 11:00:19) \n[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]'

Note from the Author or Editor:
[Not an error, no fix required, type changed to clarification]

This post refers to Python behavior that has changed since this edition was published. The new behavior appeared in Python 3.5, in September 2015. Because this June 2013 edition is based on 3.3, I'm not going to wedge this in (this edition can't accommodate every post-publication Python change). I am, however, changing this post to a clarification for other readers who may have the same query.

The example (in the second code section of page 541 in the most recent printing) fails as shown in the book on 3.3:
C:\code> py -3.3
>>> def f(a, *b, c=6, **d): print(a, b, c, d)
...
>>> f(1, *(2, 3), **dict(x=4, y=5), c=7)
SyntaxError: invalid syntax

But as you noted now works on 3.5 and later without error:
C:\code> py -3.5
>>> def f(a, *b, c=6, **d): print(a, b, c, d)
...
>>> f(1, *(2, 3), **dict(x=4, y=5), c=7)
1 (2, 3) 7 {'y': 5, 'x': 4}

This is due to a relaxing of argument ordering rules that coincides with generalized * unpackings in 3.5, and is alluded to in this post on my recent Python changes page:

http://learning-python.com/books/python-changes-2014-plus.html#s354

Mike Travagli  Nov 25, 2015  Jan 06, 2017
Printed
Page 542
The min Wakeup Call! seciton, 2nd paragraph

The paragraph says: "Moreover, the function should work for all kinds of Python object types: numbers, strings, lists, lists of dictionaries, files and even None".

I think that's not quite right, in Python 3.X (as the books says too), dictionariy comparison tests were removed, so I can't figure out out the 3 functions coded can work if they all use the `<` test.

Note from the Author or Editor:
Yes -- and good catch: these functions do work for dicts in 2.X, but not in 3.X where direct dict comparison was dropped. To clarify, on Page 542, add the following sentence to the very end of paragraph #2 under "The minWakeup Call!" that begins with "Suppose you ...", including the parenthesis, and with links to chapters 8 and 9:

"(To be fair, Python 3.X users don't need to support dictionaries, because their dictionaries don't support direct comparisons; see Chapters 8 and 9.)"

[Discussion only follows]
With the solutions shown in the book, the following code :

>>> import mins
>>> mins.min1(dict(a=1), dict(b=2))
TypeError: unorderable types: dict() < dict()

works in 2.X but fails in 3.X -- as does a built-in min(dict(a=1), dict(b=2)). This extends also to lists of dictionaries -- what the book actually mentions:

>>> mins.min1([dict(a=1)], [dict(b=2)])
TypeError: unorderable types: dict() < dict()

Dictionaries can be made to work with these functions in 3.X by converting them to key/value lists before being passed in via dict.items(), and then back to dicts on return via dict(), thereby comparing items lists (technically the values in items iterables) instead of dictionaries:

>>> dict(mins.min1(dict(a=1).items(), dict(b=2).items()))
{'a': 1}

>>> [dict(I) for I in mins.min1([dict(a=1).items()], [dict(b=2).items()])]
[{'a': 1}]

But that's strictly extra credit here.

Antonio Cota  Aug 27, 2014  Oct 29, 2014
PDF
Page 545
Code snippet

On page 545 under heading 'Generalized Set Functions': the text reads "Here is a version that intersects an arbitrary number of sequences (one or more) by using the varargs matching form *args to collect all the passed-in aguments..."

However the code example does not return an expected results. Example. s1, s2, s3 = 'spam', 'slam', 'pppp'

intersect(s1,s2,s3)
...
[ ]

Note from the Author or Editor:
[Not an error, no fix required, changed type to clarification only]

This code works correctly under both Python 3.X and 2.X, and the '[]' empty list output is correct for your example, because there are no items in common. Perhaps you misunderstood what intersection means? It's described in multiple locations: see pages 400, 480, 545, and later. The variant on page 545 picks out items in common among all arguments, which, in your test example, is none.

Also, make sure the "else" is aligned vertically with the nested "for", and not the "if", as it is associated logically with the "for" -- where it's run on the way out of the loop, if no "break" was reached. Here's the code and its behavior, sans its comments (which align poorly on this page):

def intersect(*args):
res = []
for x in args[0]:
if x in res: continue
for other in args[1:]:
if x not in other: break
else:
res.append(x)
return res

s1, s2, s3 = 'spam', 'slam', 'pppp'
print(intersect(s1, s2, s3)) # => [], no items in common

s1, s2, s3 = 'spam', 'slam', 'pappmp'
print(intersect(s1, s2, s3)) # => ['a', 'm'], 2 items in common

H Z Htat  Apr 12, 2016  Jan 06, 2017
Printed
Page 550
Why You Will Care: Keyword Arguments section, second-last paragraph

The sentence: "but also allows us to pass in optional keyword arguments to specify a dictionary sort key and a reversal flag, which.....".

I think that 'dictionary sort key' is too ambiguous, in fact according to the documentation: "key specifies a function of one argument that is used to extract a comparison key from each list element"

So in my opinion it should be more clear.

Note from the Author or Editor:
This is covered earlier, but to avoid confusion, let's add a word -- Page 55, paragraph -2, middle of line 2, change the first of these to the second:

"a dictionary sort key"
"a dictionary sort key function"

[Discussion only follows]
Actually, the book also makes this pretty clear in the full section on the subject earlier, which is back-referenced here. Did you try following the link in the book here before going to Python docs? It points to Chapter 8, where Page 247 says:
"""
the key argument gives a one-argument function that returns the
value to be used in sorting ...
"""
and then demonstrates this live. That said, a 1-word augmentation seems harmless and useful here.

Antonio Cota  Aug 27, 2014  Oct 29, 2014
Printed
Page 562
bottom (last line)

In the example of computing the minimum value. In the "goal" (p. 562) it states "the function should accept zero or more arguments...".
But the proposed code expects at least one argument.

Note from the Author or Editor:
[No reprint edits required] I'm changing this to a clarification to save it for other readers who may have the same question. This is on page 542 in earlier printings.

While the post is not incorrect, it seems to discount some of the larger points of the solution shown. The solution actually _does_ accommodate zero arguments, by letting Python detect and raise an error -- a completely valid response, given then min() really has no meaning if no arguments are passed in, and there is no valid result if any Python data type is allowed (should the result be 0? ''? None?...). In fact, this is called out by the book explicitly just after the code:

"""
Notice that none of these three variants tests for the case where no arguments are passed in. They could, but there’s no point in doing so here—in all three solutions, Python will automatically raise an exception if no arguments are passed in. [...] This is exactly what we want to happen—because these functions support any data type, there is no valid sentinel value that we could pass back to designate an error, so we may as well let the exception be raised.
"""

In other words, the zero-argument case is handled, and as best it can be. Less than fully rigorous, perhaps, but Python is not mathematics, and exceptions are not crashes -- they are a well-defined way to handle program conditions. And they are a full-points solution in my book.

Stijn Helsen  Dec 24, 2017  May 04, 2018
Printed, PDF, ePub, Mobi,
Page 567
end of footnote

We have space to augment a footnote on this page with a few key and useful summary details. On Page 567, at the end of the footnote on this page, change:
"
lambda is simpler to use than you may think.
"
to the extended:
"
lambda is simpler to use than you may think: it's simply an alternative way to code a function, albeit without full statements, decorators, or 3.X annotations.
"

Mark Lutz
 
Aug 07, 2014  Aug 22, 2014
Printed, PDF
Page 577
paragraph 3

A reader sent this by direct email. The wording of this paragraph follows Python's own help for reduce(), but is potentially confusing, as it may be read as referring to argument order, rather than values operated on in general. Let's tighten this up as follows; change:
"""
The built-in reduce also allows an optional third argument placed before the items in the sequence to serve as a default result when the sequence is empty,
"""
to this (there seems to be ample space in this paragraph to avoid repaging):
"""
The built-in reduce also allows an optional third argument, effectively placed before the items in the sequence to serve as an initial value and a default result when the sequence is empty,
"""

Mark Lutz
 
Mar 15, 2015  Mar 20, 2015
Printed, PDF, ePub, Mobi,
Page 581
3rd paragraph

The text reads:
"In fact, Python has a host of tools that most would considered functional in nature,"

It should read:
"In fact, Python has a host of tools that most would consider functional in nature,"

Note from the Author or Editor:
Yes -- a minor but valid typo (that eluded many proofs); please fix as described.

Anonymous  Feb 03, 2014  May 02, 2014
Printed
Page 581
Chapter title ("Comprehensions and Generations")

It seems to me that the chapter title "Comprehensions and Generations" should probably be "Comprehensions and Generators".

I don't believe "generation" has any significant meaning within Python syntax, and if it does, I don't think it's defined in this book. Moreover, the term "generator" appears numerous times with multiple index entries. Lastly, as a crude metric, a Google search for "python generations" returns about 800 results, whereas "python generators" returns 40,000.

Note from the Author or Editor:
[No changes required--retained as a note] No, this title was crafted this way on purpose, in the name of a form of alliteration often called assonance or end rhyme (think rap for an example). You might prefer the more technically tight "Generators" here, and I appreciate your assertion. But, being a writer, I'm prone to turn words for fun over formality this way now and then.

And FWIW, the term "generators" shows up 132 times in the book -- including a few times in the introductory paragraph immediately following the title in question. "Generations" is just in the title, and I suspect most readers get the play on words. I also suspect that a book written to strictly conform to Google search results wouldn't be one worth reading, but YMMV.

Anonymous  Apr 07, 2021  Jul 02, 2021
Printed, PDF, ePub, Mobi,
Page 591
Line 1 of last paragraph on page

Change:
"(generator expressions were available as an option as early as Python 2.2)"
To this, replacing just word #2:
"(generator functions were available as an option as early as Python 2.2)"

That is, generator _functions_ were available in 2.2, not generator _expressions_, which came online in 2.4 -- facts stated explicitly in the bullet list just above this text on the same page. A minor typo officially blamed on being distracted by writing a 1600 page book...

Mark Lutz
 
Jan 12, 2014  Jan 24, 2014
Printed, PDF, ePub, Mobi,
Page 594
first sentence, add new footnote

There's a minor extension in 3.3 regarding return statements in generator functions that was omitted in the book due to its obscurity, but has potential to confuse if it shows up in code over time. If we can squeeze it in without impacting page breaks badly, I'd like to add a clarifying footnote.

THE CHANGE:

On Page 594, add a footnote reference to the very end of the first sentence:
"""
To end the generation of values, functions either use a return statement with no value or simply allow control to fall off the end of the function body.*
"""

with this footnote text at page bottom (its "StopIteration" is literal font, and it looks like the next 2 pages have room to absorb the insert; let me know if shortening is needed to avoid changing page breaks for the rest of the chapter):
"""
* Technically, Python treats return statement values in generator functions as syntax errors in 2.X, and in all 3.X prior to 3.3. As of 3.3, a return statement value is allowed and attached to the StopIteration object, but the value is ignored in automatic iterations contexts, and using this makes code incompatible with all prior releases.
"""

Mark Lutz
 
Nov 08, 2013  Jan 24, 2014
Printed
Page 608
Last code sample

The code sample should contain __iter__ instead of __init__ method:

class SomeIterable:
def __init__(...): ... # On iter(): return self or supplemental object
def __next__(...): ... # On next(): coded here, or in another class

should be

class SomeIterable:
def __iter__(...): ... # On iter(): return self or supplemental object
def __next__(...): ... # On next(): coded here, or in another class

Note from the Author or Editor:
Yes: please change just the "def __init__(...):" to "def __iter__(...):" as suggested, retaining the double-underscores, and horizontal spacing of "#".

[Discussion] This is near the bottom of Page 631 on printings run on or after Sep-2016. This is minor detail. In this brief preview-only section, the code is in skeleton form, and the role of __iter__ is clearly described in the immediately-preceding narrative (and covered by 15 full pages later in the book, as stated). One could also argue that an __init__ is not strictly incorrect here, given that there is no content to the methods anyhow, but I won't; __iter__ was the intended name here.

Laszlo Nagy  Mar 20, 2017  Apr 21, 2017
Printed
Page 609
in section 'Example: Generating Scrambled Sequences', 2nd paragraph, 2nd line

I believe "sequences like strings and list" should read instead "sequences like strings and lists" (add "s" to "list")

Note from the Author or Editor:
Yes -- please change "list" to "lists" in the referenced clause. This is on page 632 in more recent printings. It should clearly be plural, but somehow eluded many proofs; good catch.

Anonymous  Apr 07, 2021  Jul 02, 2021
Other Digital Version
612
last line on my kindle

def permute2:

I think there should be a set of brackets around
seq [I:i+1] + x so that it reads

yield(seq[I:i+1] + x)

not sure why but I can't get it to work otherwise

Note from the Author or Editor:
[Informational only: not an errata, no change required.] This example's code (list-builder and generator-based recursive permutations) works as shown in every Python I've tested, from 3.3 down to 2.3; prior to 2.3, yield wasn't available by default:

>>> import sys; sys.version
'3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:57:17) ...'
>>> from permute import *
>>> permute1('abcd') == list(permute2('abcd'))
True
>>> len(list(permute2('abcd'))), 4*3*2*1
(24, 24)

>>> import sys; sys.version
'2.3 (#46, Jul 29 2003, 18:54:32) ...'
>>> ..identical behavior...

Yield morphed from statement to expression in 2.5 to support sends (and sometimes must be enclosed in parenthesis in this role), but it never was required to use function call-like parenthesis.

I can't tell what the issue may be, but make sure you're running the exact code in the book (or its examples zip file); the post has mixed case for variable "i" which would fail, but this may be just in the post.

For more yield background: see Pages 137-138 (expressions), 320-321 (statements), 353-354 (reserved words), and most of Chapter 20 (generators).

Stephen Gregory  Aug 07, 2013 
PDF
Page 615
code comments

In the first code snippet:
>>> from permute import permute1, permute2
>>> seq = list(range(10))
>>> p1 = permute1(seq) # 37 seconds on a 2GHz quad-core machine
# Creates a list of 3.6M numbers
Here, it creates a list of 3.6M numbers, But I have got the size of `p1` through `sys.getsizeof(p1)`. In my environment, it returns '31774880' in bytes, then convert it to megabytes, it will be around 30M numbers. There is much gap between 30M and 3.6M. So the memory usage maybe wrong.

Note from the Author or Editor:
[No change required; informational only]

I think you may be confusing the logical number of list items (referred to in the book: 3.6M) with the physical size that such a list requires in memory (not discussed in the book). The len() function returns the former, but sys.getsizeof() returns the latter--the size in memory of an object itself, but not the items it may refer to. On a Mac, using Python 3.5, the number of items in the result is 3,628,880 (3.6M) as advertised:

>>> import math
>>> from permute import permute1, permute2
>>> math.factorial(10)
3628800
>>> seq = list(range(10))
>>> p1 = permute1(seq)
>>> len(p1), p1[0], p1[1]
(3628800, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 9, 8])

But the physical size is much more difficult to account for, given the overheads of object storage in memory. The total memory space for the 3.6M-item list is 31,774,872 (31M) bytes, as you found:

>>> import sys
>>> sys.getsizeof(p1)
31774872

However, the storage of Python lists cannot be directly compared to that of an array of integers in a C-level language. In Python, even simple integers can consume 28 bytes in memory:

>>> sys.getsizeof(1)
28
>>> sys.getsizeof(p1) / sys.getsizeof(1)
1134816.857142857

This still is meaningless, though, because the list's referents are not included in its space total, and it's difficult to pin down the size of both integers and lists:

>>> sys.getsizeof(0), sys.getsizeof(1)
(24, 28)
>>> sys.getsizeof([]), sys.getsizeof([1]), sys.getsizeof([0, 1, 2, 3])
(64, 72, 96)

If you stare at this long enough, you'll probably notice a pattern: lists seems to add 8 bytes for every item after the first:

>>> sys.getsizeof([0] * 10)
144
>>> (sys.getsizeof([0] * 10) - 72) / (10-1)
8.0

This can be used on the permute result list's length to get closer to the physical memory size reported by getsizeof():

>>> len(p1) * 8
29030400
>>> sys.getsizeof([1]) + ((len(p1)-1) * 8)
29030464
>>> sys.getsizeof(p1)
31774872

But it's still not enough (29M versus 31M). Lists use optimization techniques internally (like increasing over-allocation to allow for future expansion), and you'd have to study the C source-code implementation to truly understand the space used--which is why this topic wasn't covered in the book. As an approximation, it's fairly safe to say that a list of N items takes roughly 8 bytes per item, plus some extra overhead. Hence the 31M space for 3.6M items, though finer-grained precision requires finer-grained analysis.

Incidentally, this example still takes about the same amount of runtime today, on a 2015 Macbook Pro running a Core I5 (the book's 37 seconds was on an older I7 from 2012 on Windows; at least in terms of raw CPU speed, times haven't changed much):

>>> import time
>>> def run():
... start = time.perf_counter()
... p1 = permute1(seq)
... print(time.perf_counter() - start)
...
>>> run()
35.37005175699596

zhenguoli  Feb 09, 2017  Apr 21, 2017
Printed
Page 617
"Example: Emulating zip and map with Iteration Tools" section

The example:
map(lambda x, y: x+y, open('script2.py'), open('script2.py'))
['import sys\nimport sys\n', ....................., ...etc...]

I think that there should be a 'list()' call wrapping the map function to return the list shown.

Note from the Author or Editor:
Yes, the list() is required, though only for 3.X (this code was likely from a 2.X session,as it's new in this edition). Given that all other code in this section uses 3.X form, lets change as suggested -- Page 617, 1st code listing, line -4, change from the first of the following to the second, adding just the enclosing list(...), and retaining the bold code font:

>>> map(lambda x, y: x + y, open('script2.py'), open('script2.py'))
>>> list(map(lambda x, y: x + y, open('script2.py'), open('script2.py')))

Antonio Cota  Sep 08, 2014  Oct 29, 2014
Printed, PDF, ePub, Mobi,
Page 617, 618
assorted (three updates)

Three minor edits, inspired by a reader's email described ahead, and largely for Python 2.X readers to alert them to a version difference:

1) On page 617, extend the end of the first paragraph on the page, from:
"""
zip pairs them up:
"""
to this, with "zip", "map", and "None" in literal font:
"""
zip pairs them up (3.X's map truncates shorter iterables; 2.X pads them with None):
"""

2) On Page 617, extend line 13 of code listing section #1, from:
"""
>>> list(map(pow, [1, 2, 3], [2, 3, 4, 5])) # N sequences: N-ary function
"""
to this, adding ", 3.X" and retaining the current vertical alignment of the "#":
"""
>>> list(map(pow, [1, 2, 3], [2, 3, 4, 5])) # N sequences: N-ary function, 3.X
"""

3) On page 618, at the start of code listing section #4 that reads:
"""
# Using generators: yield and (...)

def mymap(func, *seqs):
res = []
for args in zip(*seqs):
yield func(*args)
"""
delete the entire 3rd line that reads "res = []" (only). This line was copied from the prior version, and is not needed in this version. The code should now read as follows (and hopefully doesn't change paging too much):
"""
# Using generators: yield and (...)

def mymap(func, *seqs):
for args in zip(*seqs):
yield func(*args)
"""


[DISCUSSION only follows:]

A reader wrote to ask why this example on Page 617 of Chapter 20:

>>> list(map(pow, [1, 2, 3], [2, 3, 4, 5])) # N sequences: N-ary function
[1, 8, 81]

works on Python 3.X, but fails in 2.X. This reader later withdrew the query, after finding the earlier 2.X map() coverage which notes its None padding when argument lengths differ (by contrast, 3.X's zip() and map() both truncate). This earlier coverage is on Page 408-409, in Chapter 19's section "map equivalence in Python 2.X."

In hindsight, though, the Chapter 19 section (and related material later in Chapter 20) is perhaps not as clear about the 2.X/3.X differences in the map() call as it could have been. Really, the padding with None always occurs in 2.X, _irrespective_ of the function argument passed in. In full detail:

* IN PYTHON 3.X, map() always truncates at the shortest argument's length, and a real function is expected in its first argument:

C:\...>py -3
>>> list(map(pow, (2, 3), (1, 2, 3))) # 2**1, 3**2
[2, 9]

>>> list(map(pow, (2, 3, 4), (1, 2))) # 2**1, 3**2
[2, 9]

>>> list(map(None, (2, 3), (1, 2, 3)))
TypeError: 'NoneType' object is not callable

* IN PYTHON 2.X, map() always pads shorter arguments with None, regardless of whether a real function or None is passed -- which can lead to errors for functions that don't expect the None:

C:\...>py -2
>>> list(map(pow, (2, 3), (1, 2, 3))) # 2**1, 3**2
TypeError: unsupported operand type(s) for ** or pow(): 'NoneType' and 'int'

>>> list(map(pow, (2, 3, 4), (1, 2))) # 2**1, 3**2
TypeError: unsupported operand type(s) for ** or pow(): 'int' and 'NoneType'

>>> list(map(None, (2, 3), (1, 2, 3)))
[(2, 1), (3, 2), (None, 3)]

This is why Page 617's "list(map(pow, [1, 2, 3], [2, 3, 4, 5]))" works in 3.X (as shown) but fails in 2.X (as not shown): on 2.X, the last function call runs "None ** 5" and fails.

In addition to 2.X map() coverage on Page 408-409, this 2.X behavior is strongly implied by the manual zip() and map() implementations that immediately follow in Chapter 20. Still, the extension to a real function argument isn't stated explicitly in either location.

Also note that the 2.X-flavor map() implementation in Chapter 20's section "Coding your own zip(...) and map(None, ...)" is really just that of 2.X's map(None,...), as it does not apply a function to paired items (though you could easily extend it to do so). This example primarily implements a zip() with padding.

For more on map() in Python 3.X and 2.X, see also Python Pocket Reference 5th Ed. (a supplement to Learning Python), as well as Python's standard library manual. As a tutorial, Learning Python occasionally sidesteps some obscure or dated fine points on purpose; this qualifies on both counts (mapping functions on differing-length iterables in 2.X seems rare in the extreme), but the book example's potential to confuse merits the patches.

Mark Lutz
 
Jan 05, 2015  Jan 09, 2015
Printed
Page 619
In the code for gen() generator

I think, you should insert “print(i)” before “X=yield i” to be compatible with the outputs below the code.

Note from the Author or Editor:
[No changes required]. No, the output matches the existing code exactly. Have you tried running it yourself? It's complex because the output reflects _both_ the explicit print(), and the automatic display of return values at the interactive prompt. Let's dissect the code; the initial def:

>>> def gen():
... for i in range(10):
... X = yield i
... print(X)

makes a generator that will both yield results, and receive and display values sent from a caller. When the generator itself is called, it doesn't run the def's code, but simply makes and returns an iterable object with a send() method:

>>> G = gen()

On the first next(), the generator starts the for loop and runs up to the first yield; the yielded value is then automatically displayed at the interactive prompt as usual ("0"):

>>> next(G)
0

But here's the odd bit: when a value is sent to the generator by send(), the generator resumes execution after the yield, which also returns the sent value; the sent value is assigned to X and explicitly printed within the generator ("77"); but the generator then proceeds on to the next for-loop iteration and yields the next result, which is automatically printed at the interactive prompt ("1"):

>>> G.send(77)
77
1

And so on for later sends (if next() is called instead, it simply sends None):

>>> G.send(88)
88
2
>>> next(G)
None
3

Trace on your own if this still isn't making sense. I'm retaining this reply as a clarification for other readers who may be confused by this example too. Generators are mind-bending stuff on a good day.

Rudolf Gilánik  Jan 08, 2020 
Printed, PDF, ePub, Mobi,
Page 621
1st paragraph

The text reads:
"..raised by the next(it) inside the comprehension..."

It should read:
"..raised by the next(i) inside the comprehension..."

Note from the Author or Editor:
Yes--this reference is abstract, but it should be "i" to match the preceding code. Please change as described. Note that this is In line 2 of page 622 (not 621) in all the books and pdfs, including the latest Jan 2014 4th printing.

Anonymous  Feb 04, 2014  May 02, 2014
PDF
Page 621
def myzip(*args): iters = map(iter, args) while iters: Generator Functions and Expressions

The implicit code for the zip emulator in this section seems broken on my version of Python.

Python 3.7.4 (default, Jul 9 2019, 16:32:37)
[GCC 9.1.1 20190503 (Red Hat 9.1.1-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def myzip(*args):
... iters = list(map(iter, args))
... while iters:
... res = [next(i) for i in iters]
... yield tuple(res)
...
>>> list(myzip((1, 2, 3), (4, 5, 6)))
Traceback (most recent call last):
File "<stdin>", line 4, in myzip
File "<stdin>", line 4, in <listcomp>
StopIteration

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: generator raised StopIteration

Note from the Author or Editor:
[No book changes required] Yep; this reflects a major and dubious change to generator functions made in Python 3.7 which broke much existing code--including this book example. In short, in 3.7 and later, StopIteration can no longer be raised--either implicitly or explicitly--to end a generator. Instead, generators:

1) Must use "return" statements (or, equivalently, fall off a function's body) to terminate the results generation

2) May need to explicitly catch StopIteration (and possibly RuntimeError) instead of allowing it to propagate as in the example

For the fuller story, see the writeup on this at my Python-changes page here:
https://www.learning-python.com/python-changes-2014-plus.html#s372

It's not clear if or how this can be addressed in book reprints--or whether it should be at all. Keeping up with all Python 3.X thrashing seems a monumental task for a print book. Given the lack of space for inserts on these pages, for now I'm going to relegate this to the web page listed above, and also refer readers to Python's "What's New" documents for this and other Python churn (and add this to a growing list of updates should a future edition ever come together).

Anonymous  Sep 13, 2019  Nov 22, 2019
Printed, PDF, ePub, Mobi,
Page 624
3rd or last paragraph

The text reads:
"Here are statement-based equivalents of the last two comprehensions
(though they differ in that name localization):"

Maybe its just me, but the parenthetical note is either is either cut-off short, as in "though they differ in that name-localization thing we just talked about", or maybe it should just be rendered as "though they differ in name localization"

Note from the Author or Editor:
Yes--a minor typo as is ("that" was supposed to be "their"). But for more clarity, let's change the parenthesized part to read:
"(though they differ in their name localization, per the prior section):"

Anonymous  Feb 04, 2014  May 02, 2014
Printed
Page 629
Timing Iteration Alternatives - Second Paragraph

The book: "The generator functions and expressions of the preceding chapter tend to be slightly slower than list comprehensions, though they minimize memory space requirements and DON'T delay result generation."

I think that "don't"' shouldn't be there, because as the book said previously generators delay results.

Note from the Author or Editor:
No, the text as intended is correct here (see below). But to clarify, let's change the end of the 2nd last paragraph on Page 629 from the first of the following to the second (hopefully this won't change page breaks here; please contact me if it does):

"don’t delay result generation."
"don’t delay the caller for result generation when there are many results to generate."

[Discussion only follows]
Not an errata, though I understand the confusion. Generators do produce results only on request. as covered in the entire preceding chapter. However, the "don't delay" here is referring to the fact that generators won't pause a program to produce results when there are many in the results set, or it takes a long time to produce them; generators return the first result quickly, rather than making the caller wait for the entire results set to be computed all at once.

If that's unclear, please see the permutations case study in the prior chapter, Pages 612-616 -- a non-generator pauses the caller for nontrivial lengths (and perhaps forever), but the generator returns the first result quickly; quoting from this:
"""
The list and generator versions' results are the same, though the generator minimizes both space usage and delays for results.
"""
This was what the "don't delay" meant here.

Antonio Cota  Sep 10, 2014  Oct 29, 2014
Printed
Page 639
Line 9, in the example code of timer2.py

I think there are two typos in the example code. The line 9 in the book is:
bestoftotal(spam 1, 2, a=3, b=4, _rep1=5, _reps=1000) runs best-of-totals

I think it should be:
bestoftotal(spam, 1, 2, a=3, b=4, _reps1=5, _reps=1000) runs best-of-totals
i.e. there should be ',' between spam and 1 and _rep1 should be _reps1.

Note from the Author or Editor:
Yes: just a program comment and not working code, but worth a fix.
Edit as described, changing:
bestoftotal(spam 1, 2, a=3, b=4, _rep1=5, _reps=1000) runs best-of-totals
to:
bestoftotal(spam, 1, 2, a=3, b=4, _reps1=5, _reps=1000) runs best-of-totals

(And good catch; this has been read many times without noticing the typos -- even while patching another typo in this very same line in the first reprint.)

Niilo Siljamo  Dec 08, 2014  Jan 09, 2014
Printed
Page 652
About 1/3 from the top

The code uses time.clock() and refers to Python 3.3. However that function was deprecated in 3.3 (https://docs.python.org/3.3/library/time.html#time.clock) and removed in 3.8 (https://github.com/python/cpython/pull/13270).

Note from the Author or Editor:
[No changes required] I appreciate your input, but this book was written when Python 3.3 was current, and lots of people were using both older versions of 3.X as well as 2.X that didn't have the new tools. Some still do today, despite what you may have heard.

The deprecation status of these time-module functions is clearly described in the sidebar on pages 655-656, which is referenced earlier at the top of page 654 (did you miss it?). That sidebar also gives coding work-arounds to address future deprecation, which, as you note, is now officially complete. Computer books cannot predict the future, but in this case, it was fairly well foreshadowed.

But there's a larger point here. If, as some might wish, this book were updated to include only today's latest and greatest, it would both exclude those using older versions, and be lamented as dated just a few years hence. This requires compromises in both camps. Unfortunately, those bent on changing Python incompatibly have never seemed to be interested in compromise; more on this here:

https://learning-python.com/python-changes-2014-plus.html#s381

That said, time-module differences are ultimately trivial compared to the task of mastering programming at large. Most beginners are best served by learning the fundamentals covered by this book first, and exploring the transient cutting edge later, if and when needed. The details of running aren't all that important until you've learned to walk.

Boris Pulatov  May 06, 2020 
Printed
Page 665
Exercise 10

The given filename has a typo.
"timerseqs.py" should be
"timeseqs.py"

Note from the Author or Editor:
This is on line 4 of the writeup for exercise #10, and on page 688 in recent printings. Yes: change the referenced "timerseqs.py" to timeseqs.py". This filename is incorrect in this sole location, but appears correctly in about a dozen other places (including derivatives); the typo should hopefully be obvious to most readers but merits a patch.

Timothy Haven  Oct 13, 2019  Nov 22, 2019
Printed
Page 679
First sentence after the list

Ultimately, the concatenation of these four components becomes sys.path...

should be:

Ultimately, the concatenation of these five components becomes sys.path...

because the list on the top of the page contains 5 (not 4) items.

Note from the Author or Editor:
Yes, change "four components" to "five components", as suggested. (The fifth item was added to the list preceding this text in this edition. There's probably a joke about Holy Hand Grenades in here somewhere, but we'll leave that aside....).

Laszlo Nagy  Dec 11, 2015  Sep 02, 2016
Printed
Page 680
3rd paragraph, last line

located in usr/local/lib/python3.3/site-packages ... should be
located in /usr/local/lib/python3.3/site-packages ...
with a slash (/) at the beginning of the path.

Note from the Author or Editor:
(This is on page 702, paragraph -2 in printings after July, 2016.) Yes, add the leading "/" to "usr/local/lib/python3.3/site-packages" here. All other appearances of such paths use a leading slash, per Unix syntax.

Eckhard Stein  Jul 20, 2016  Sep 02, 2016
Printed
Page 695
3rd bullet point under "Files Generate Namespaces"

"accessed via the attribute__dict__"

should be

"accessed via the attribute __dict__"

(there's a space missing between "attribute" and "__dict__")

Note from the Author or Editor:
Yes -- we need to add a space before the "__dict__" in this text. This is on page 719 in newer printings, in the first line of bullet #3. It's difficult to notice the missing space visually, but a copy/paste from both old and new PDFs makes the omission clear. Hopefully, this won't change pagination much.

Anonymous  Apr 12, 2021  Jul 02, 2021
Printed
Page 701
In the paragraph below the list of 5 components

In my opinion the sentence “The first and third elements of the search path are defined automatically.” should be changed to “The first, third and fifth elements of the search path are defined automatically.”

Note from the Author or Editor:
REPRINTS: please change the start of the referenced sentence from:
The first and third elements
to:
The first, third, and fifth elements
This is on page 679 in earlier reprints; hopefully, it won't change page breaks. Note the second comma in the three-item series; I coded it this way on purpose, because the book uses this style as a rule, and I prefer it in general (if ORM's style guide differs, we should probably ignore it for consistency).

DISCUSSION: this seems a bit nitpicky, but in the text following this, the first, third, and fifth items on the list are labeled as "(automatic)" and this opening statement should probably match. This is not, however, a technical mistake; post type changed to language change.

Rudolf Gilánik  Jan 08, 2020  Jun 05, 2020
Printed
Page 703
2nd paragraph in the "Why You Will Care: Module Reloads" box

this is very minor, but it would be better to somehow prevent a linebreak in the middle of "C++"

Note from the Author or Editor:
[No changed needed, retained as a note] This occurs on page 703 of older (2015 and earlier) printings only, where its "C++" is split badly by a "\n" newline like this: "C+\n+". This splitting is not present in newer (2016+) printings, where this is on page 727, and its "C++" appears atomically.

Moreover, "C++" is not line-split at any of its 93 occurrences in the latest printings. For the record, the bad "C++" split on page 703 is also the only one of the 93 appearances broken this way in older printings, so this was a rarity.

I agree this is subpar where it crops up, and line splits have been problematic in the past, especially for literal terms. But this particular instance was repaired in later reprints, so there's no action to take on this.

Anonymous  Apr 12, 2021  Jul 02, 2021
Printed
Page 704
In the second paragraph of "The sys.path List" section

Similar as the mistake on page 701, please correct the sentence
"Python configures it at program startup,automatically merging the home directory of the top-level file (or an empty string to designate the current working directory), any PYTHONPATH directories, the contents of any .pth file paths you’ve created, and all the standard library directories.”
to
“Python configures it at program startup,automatically merging the home directory of the top-level file (or an empty string to designate the current working directory), any PYTHONPATH directories, the contents of any .pth file paths you’ve created, all the standard library directories and the site-package home directory.”

Note from the Author or Editor:
REPRINTS: please change the referenced sentence in full to this:
"""
Python configures it at program startup, merging the home directory of the top-level file (or an empty string to designate the current working directory), any PYTHONPATH directories, the standard library's directories, the contents of any .pth files, and the site-packages directory.
"""
In this, "PYTHONPATH" is literal format, and ".pth" and "site-packages" are filename. This was tight on space, so some rewording was applied to avoid page-break changes; please verify that it worked. Here again, the last comma in the series should stay as coded.

DISCUSSION: This seems a bit rigid too: the existing statement isn't complete but also isn't wrong, and readers just saw the whole path story in detail. Still, for people who chafe at anything short of mathematical completeness (and we know who we are), we'll reword to match the prior section's five-component coverage. Besides adding site-packages per the poster's suggestion, the new version also matches the component ordering of both Python and the prior section (hey, if you're going to be rigid, why not be rigid about it?).

Rudolf Gilánik  Jan 08, 2020  Jun 05, 2020
Printed
Page 709
Last line in the footnote

The last line in the page 709 is:
“No module named 'm.py'; m is not a package.”

I think it should be:
“No module named 'mod.py'; 'mod' is not a package.”

Note from the Author or Editor:
Yes -- an abstract description, but it should probably match the preceding abstract description (though it's more typo than technical mistake). There are extra quotes in the suggested edit, however, that should not be added.

EDIT: In the referenced line, change:
No module named 'm.py'; m is not a package.
to this (keeping the original enclosing curly quotes):
No module named 'mod.py'; mod is not a package.

Niilo Siljamo  Feb 05, 2015  Mar 20, 2015
Printed
Page 724
Imports within packages Section

The text indicates the path test\pkg\__init__.py but watching the following examples i think it should be code\pkg\__init__.py

Note from the Author or Editor:
Yes -- a typo (probably apparent from "code" in so much surrounding context, but worth fixing). At the very end of the second line under header "Imports Within Packages", change:
but empty test\pkg
to:
but empty code\pkg

Antonio Cota  Jan 27, 2015  Mar 20, 2015
Printed, PDF, ePub, Mobi,
Page 735
Page 735 (para -1, line 2/3), and 736 (para 5, line 2)

Page 735 calls sys.path the module search path. That's true generally and acceptable informally, but the search path is really only sys.path at the top (left) in absolute imports; in other cases, it may be limited to a package's location, whether that is a regular package's sole directory or a namespace package's __path__.

This should be apparent given the preceding description of package and relative imports and later discussion of __path__ and sys.path's leftmost role, but this terminology may be a bit too loose here, especially following the sections on packages and preceding a formal import algorithm sketch for namespaces.

THE CHANGES:

1) To clarify, in Page 735's paragraph -1 line 2, change this sentence:
"
During imports, Python still iterates over each directory in the module search path, sys.path, just as in 3.2 and earlier.
"
to this, expanding the "sys.path" in the middle, retaining the literal font on "sys.path", and using a normal dash for both of the "--":
"
During imports, Python still iterates over each directory in the module search path--defined by sys.path for the leftmost components of absolute imports, and by a package?s location for relative imports and components nested in package paths--just as in 3.2 and earlier.
"

2) Also, in Page 736's paragraph 5 line 2, change:
"
located via multiple sys.path entries.
"
to this (with no fixed-width font):
"
located via possibly multiple module search path entries.
"

It looks like we have space for both changes without impacting page breaks, but let me know if this won't work.

Mark Lutz
 
Nov 06, 2013  Jan 24, 2014
Printed
Page 738
First paragraph

Was does the book mean with "the timing of path extensions is irrilevant"?

Note from the Author or Editor:
[No changes required - informational only]
This is subtle, but I'll try to clarify here. The full text referred to is:
"""
This is also true if we import through the namespace package name immediately—because the namespace package is made when first reached, the timing of path extensions is irrelevant:
"""
Look closely at the code listings before and after this. The code before imports a root directory (sub), and the code after imports subfolders of the root (sub.mod). The point of this text is that it makes no difference when the subfolder extension is imported or referenced; the root is still a fully-formed namespace package either way. Minor, perhaps, but if this were not so, we'd have to import a root before any of its parts. Also note that namespace packages were new in 3.3, and may or may not see wider use with time.

Antonio Cota  Jan 29, 2015  Mar 20, 2015
Printed
Page 738
Last paragraph

The word 'package' is duplicated:

once a package namespace pack-age is created,

should be:

once a namespace pack-age is created,

Note from the Author or Editor:
This is in the last paragraph on page 763 in recent printings. Yes -- please change the "package namespace package" to "namespace package" as suggested (and let me know if this changes paging in this region). And to the poster: good catch; I actually reread this section very recently myself, and didn't spot the duplicate.

Laszlo Nagy  Aug 17, 2017  Oct 27, 2017
Printed, PDF, ePub
Page 752, 754
line 12 of code listing (752), first paragraph (754)

Not an error, but to avoid confusion, I'd like to edit and call-out an assert usage:

1) In the code listing on page 752, change line 12 from the first of the following to the second, only deleting the single outermost "()" pair (and leaving a single space after "assert"):

assert(digits.isdigit())
assert digits.isdigit()

2) On Page 754, add the following sentence to the end of first paragraph on the page (that ends with "the command line."), with its "assert" in literal font, and a link to Chapter 34:

"For more on the assert statement used here, see Chapter 34."

[Discussion] This code is valid and works as is (the extra outer parens simply enclose an expression), but seems potentially misleading, as it makes assert look like a built-in function; it's a statement, as shown and explained in detail elsewhere (see pages 321, 869, 939, 1086, and primarily 1112-1113).

Mark Lutz
 
Oct 24, 2014  Oct 29, 2014
Printed
Page 764
4th line of program code

The word 'module' is duplicated:

Call reload_all with one or more imported module module objects.

should be:

Call reload_all with one or more imported module objects.

Note from the Author or Editor:
Yes--please remove the extraneous "module". This is comment text only, but worth the fix.

Laszlo Nagy  Feb 16, 2016  Sep 02, 2016
Printed, PDF, ePub, Mobi, , Other Digital Version
Page 770
Second code listing section, lines 2-6

This interactively-entered code tries to prove order-neutral equality of script output lines using sets. As is, though, the code makes and compares sets of characters instead of lines. Recall that set() takes any iterable--in this case using the string returned by file.read, which is an iterable of individual characters, not lines. This works as shown, but only by luck: it proves output character equivalence, not output line equivalence. We should use file.readline's iterable of line strings.

To improve, replace these 5 code listing lines:

>>> res1 = os.popen('reloadall.py tkinter').read()
>>> res2 = os.popen('reloadall2.py tkinter').read()
>>> res3 = os.popen('reloadall3.py tkinter').read()
>>> res1[:75]
'reloading tkinter\nreloading tkinter.constants\nreloading tkinter._fix\nreload'

with the following 5 lines, changing the 3 "read()" to "readlines()", and changing the last two lines completely; the remainder of the listing is correct as is (note: the 4 code parts following ">>>" prompts must be bold font, per the book's convention):

>>> res1 = os.popen('reloadall.py tkinter').readlines()
>>> res2 = os.popen('reloadall2.py tkinter').readlines()
>>> res3 = os.popen('reloadall3.py tkinter').readlines()
>>> res1[:3]
['reloading tkinter\n', 'reloading sys\n', 'reloading tkinter._fix\n']

Mark Lutz
 
Jun 30, 2013  Aug 16, 2013
Page 774
the first code snippet

reload(module) won't work here because "module" is not available in this scope.
The code needs clarification like 'the code before ellipsis goes in mod1 and the code after ellipsis goes in mod2 except the last line. The last line with "X" is in the mod1'.

Note from the Author or Editor:
[No edits required] (This is on page 798 in later printings) You're right that the snippet shown would require an 'import module' to run. But this isn't real code - note its 3 dots where more code should be; the only thing it's meant to illustrate is that names loaded by 'from' are not updated by a later 'import'; and the requirement for the 'import module' is called out explicitly and in detail by the section just two paragraphs ahead on this page.

So, agreed that this example is less than complete, but I'd rather not get off topic in this snippet, and steal the following section's thunder. Really, this was in part an admittedly subtle but deliberate setup for the next section. It also has nothing to do with different modules, by the way; this phenomenon can occur in the same module namespace. Your follow-up clarified this.

Ivan Samodurov  Jul 10, 2022  Jul 15, 2022
Page 774
the first code snippet

Addition to my previous message about "reload(module)".
There is much easier fix. Right after "from imp import reload" there should be "import module" just like in the next section of the book.

Note from the Author or Editor:
[No edits required] Agreed, but please see my reply to your initial post.

Ivan Samodurov  Jul 10, 2022  Jul 15, 2022
PDF
Page 786
Topic of MRO in general.

Great book! And, this is a tiny nit (btw):

This is the page (786) where you introduce the MRO of inheritance and the concept of Left To Right. I could not figure out what decided Left to Right order because there was no code example or explanation until page 789. At first, I glanced ahead a bit to see what I could find, but, did not get to page 789. Google returned this page: "python-history.blogspot.com/2010/06/method-resolution-order.html" (nice explanation)

Maybe a footnote 786 on what determines the let to right ordering or pointer to page 789.

Note from the Author or Editor:
[No reprint edits required] I'm retaining this as a clarification for other readers. This is on page 810 in newer printings.

I understand the post's confusion, but as a friendly suggestion: readers would probably do better by reading (or flipping) three pages ahead in their book, than by bringing up a browser and crawling through the web looking for answers. There's some good stuff online, of course, but judging from my own search results lately, you're just as likely to be misled there as informed. The book's designed to be a linear read; if you stick with it, chances are good that your questions will be answered very soon after they come up.

If you're really in the mood for something online though, a search for "mro" at my website also turns up the following (alas, these are a few pages down in a generic search; I really have to talk to the SEO folks...):

http://learning-python.com/python-newstyle-inheritance.html
http://learning-python.com/book-queries-lp5e.html#q1
http://learning-python.com/PyRef5E-preview--Formal-Inheritance-Rules.pdf

David Botham  Dec 19, 2017  May 04, 2018
Printed
Page 792
bottom of page in transitive_reload function

reloadall3.py seems to have a coding error. I believe the idea is to reload modules without duplicating any reloads. As written it duplicate reloads. For example, when I run "python reloadall3.py os" the output I get is: "reloading os, reloading ntpath, reloading genericpath, reloading stat, reloading stat, reloading sys, reloading stat, reloading sys, reloading abc" Here is my suggested fix: change line from:
modules.extend(x for x in next.__dict__.values() if type(x) == types.ModuleType and x not in visited)
to:
modules.extend(x for x in next.__dict__.values() if type(x) == types.ModuleType and x not in visited and x not in modules)

Note from the Author or Editor:
REPRINTS: please completely replace the last 8 lines of the example at the bottom of page 792 with the following 9 lines, retaining all whitespace (the "#" should align) and marking all "#" comments as italic as usual in this book; if this adds a page break, I'll shorten to fit:

def transitive_reload(modules, visited):
while modules:
next = modules.pop() # Delete next item at end
if (type(next) == types.ModuleType # Valid module object?
and next not in visited): # Not already reloaded?
status(next) # Reload this, push attrs
tryreload(next)
visited.add(next)
modules.extend(next.__dict__.values())

DISCUSSION: you're right (and great catch! - I wish this had been reported earlier). As coded in the book originally, reloadall3 can indeed reload a module more than once, because it checks only modules already visited, not those currently scheduled to be visited on the stack. Because a module is never again checked for prior visits once it's scheduled for reloading, modules that are rescheduled before they are reloaded will be reloaded redundantly. Though uncaught in testing and somewhat dependent on ordering, this can happen anytime a module is imported by multiple others:

def transitive_reload0(modules, visited): # 0: Original version
while modules:
next = modules.pop()
status(next)
tryreload(next)
visited.add(next)
modules.extend(x for x in next.__dict__.values()
if type(x) == types.ModuleType and x not in visited)

def reload_all0(*modules):
transitive_reload0(list(modules), set())

Your proposed solution works, because it checks the already-scheduled stack too, before extending it. This prevents the reschedules, and hence the duplicate reloads:

def transitive_reload1(modules, visited): # 1: Reader's working fix
while modules:
next = modules.pop()
status(next)
tryreload(next)
visited.add(next)
modules.extend(x for x in next.__dict__.values()
if type(x) == types.ModuleType
and x not in visited and x not in modules)

def reload_all1(*modules):
transitive_reload1(list(modules), set())

If pressed, though, I'd say that it seems a bit gray to check for membership in a list while it is in the process of being extended. The generator passed to extend() yields one item to tack onto the list at a time - while also checking the contents of the list. That works, but seems very implicit (if not implementation dependent). I'd rather avoid the ambiguity and drama, and move the visited test up as follows, to trap repeats before their reload is attempted. This also _might_ be marginally faster because it trades list scans for set hashing, but benchmarking results are extra credit here:

def transitive_reload2(modules, visited): # 2: Avoid 'in' during 'extend'
while modules:
next = modules.pop()
if next in visited: continue
status(next)
tryreload(next)
visited.add(next)
modules.extend(x for x in next.__dict__.values()
if type(x) == types.ModuleType)

def reload_all2(*modules):
transitive_reload2(list(modules), set())

Better still: the following coding is much closer in structure to the recursive reloadall2 version that precedes it in the book, and hence better illustrates the real recursive-versus-stack point of this section. It also traps non-module arguments at the top level; neither the original nor the two other alternatives above do. Hence, this is how this example will be patched in future reprints of the book (see REPRINTS above):

def transitive_reload3(modules, visited): # 3: Symmetry, catch non-mods
while modules:
next = modules.pop()
if (type(next) == types.ModuleType
and next not in visited):
status(next)
tryreload(next)
visited.add(next)
modules.extend(next.__dict__.values())

def reload_all3(*modules):
transitive_reload3(list(modules), set())

When tested, all three recodings fully avoid the duplicate reloads and produce the same results, though visitation order varies slightly as expected (you can run all this code live by grabbing a copy from https://learning-python.com/reloadall3.py):

# prior API compatibility
reload_all = reload_all3

if __name__ == '__main__':
# self-test
import os, tkinter, reloadall3

for test in (os, tkinter, reloadall3):
print('\n%s\n[%s]' % ('-' * 40, test.__name__))

for ra in (reload_all0, reload_all1, reload_all2, reload_all3):
print('\n<%s>' % ra.__name__)
ra(test)

RELATED NOTE: when this example is run today, Python 3.8 and 3.7 generate a deprecation warning that:

"the imp module is deprecated in favour of importlib"

Alas, Python's module API has been a moving target for some time now (per the book, reload() used to be a built-in function in 2.X), and 3.X has a now-long history of arbitrary and opinion-based changes like this that rudely break existing code.

In this case, reload() has been pointlessly relocated _twice_ in 3.X, and this example will probably also require changing "imp" to "importlib" in the near future to avoid failing altogether with an exception. This is too much change to patch in reprints, so this note will have to suffice.

On the other hand, recursion coding concepts illustrated by this example are still as relevant to the art of programming as ever. Small details like module names do morph, but the larger fundamentals presented by this book don't.

Phil Stodick  Mar 15, 2020  Jun 05, 2020
PDF
Page 794
the lines of code for res1, res2, res3

Hi,
I was not able to load the following scripts using the default download script provided. i am using python 3.6.1 and 3.3.0 on Ubuntu 16.04.2 to test.

Errors includes, unable to execute permission, unable to find script/script not found (even when running python on the code directory) and unexpected syntax error even when i was using the downloaded file.

>>> import os
>>> res1 = os.popen('reloadall.py tkinter').readlines()
>>> res2 = os.popen('reloadall2.py tkinter').readlines()
>>> res3 = os.popen('reloadall3.py tkinter').readlines()
>>> res1[:3]
['reloading tkinter\n', 'reloading sys\n', 'reloading tkinter._fix\n']
>>> res1 == res2, res2 == res3
(False, False)
>>> set(res1) == set(res2), set(res2) == set(res3)
(True, True)

Below are the modifications made in order to run the scripts.

1. change the permission for the scripts
chmod 755 reloadall*.py

2. add or replace the shebang for the scripts to include the one below:-
#!/usr/bin/env python

3. when reissue the command on 3.3, i got exactly the same results as the book.
Python 3.3.0 (default, Mar 23 2017, 10:06:39)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> res1 = os.popen('~/code/reloadall.py tkinter').readlines()
>>> res2 = os.popen('~/code/reloadall2.py tkinter').readlines()
>>> res3 = os.popen('~/code/reloadall3.py tkinter').readlines()
>>> res1 == res2, res2 == res3
>>> res1[:3]
['reloading tkinter\n', 'reloading _tkinter\n', 'reloading warnings\n']
>>> res1 == res2, res2 == res3
(False, False)
>>> set(res1) == set
False
>>> set(res1) == set(res2), set(res2) == set(res3)
(True, True)

but when i enter in for 3.6.1 i got the following instead. Where the code output for >>> res1 == res2, res2 == res3.

Python 3.6.1 (default, Mar 22 2017, 20:49:10)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> res1 = os.popen('~/code/reloadall.py tkinter').readlines()
>>> res2 = os.popen('~/code/reloadall2.py tkinter').readlines()
>>> res3 = os.popen('~/code/reloadall3.py tkinter').readlines()
>>> res1 == res2, res2 == res3
(True, False)
>>> set(res1) == set(res2), set(res2) == set(res3)
(True, True)

Thank you.

Note from the Author or Editor:
[No changes required: informational only]

There's not enough detail to be sure, but it looks like some of the errors you saw may have been caused by the DOS-endlines encoding of the examples distribution package. Unix shells don't generally like these, despite correct #! lines and chmod permissions. Try running the examples through a dos2unix command line (or similar endline conversion technique). Simpler still, just give an explicit Python in the command lines that os.popen() runs; this works fine for me on Mac OS X under Python 3.5 (Ubuntu should be similar):

/.../LP/5E/Examples/lp5e-code-1.0-jun1813/code$ python3
>>> import os
>>> res1 = os.popen('reloadall.py tkinter').readlines() # fails on Unix
/bin/sh: reloadall.py: command not found
>>>
>>> res1 = os.popen('python3 reloadall.py tkinter').readlines()
>>> res2 = os.popen('python3 reloadall2.py tkinter').readlines()
>>> res3 = os.popen('python3 reloadall3.py tkinter').readlines()
>>> res1[:3]
['reloading tkinter\n', 'reloading _tkinter\n', 'reloading re\n']
>>> res1 == res2, res2 == res3
(False, False)
>>> set(res1) == set(res2), set(res2) == set(res3)
(True, True)

As for a difference in output under Python 3.6: I cannot test at the moment, but please verify that your #! lines all use the same Python, or use an explicit "python3" (or "python") per above. It seems more likely that you're comparing different Python's outputs. It's possible that 3.6 tweaked the import machinery in ways that alter reload() results (alas, imports are a perennial target for Python developers), but that's a secondary possibility.

Anonymous  Mar 22, 2017  Apr 21, 2017
Printed
Page 803
2nd paragraph

Last sentence in 2nd para reads: 'Sometimes we call this act of replacing attributes by redefining them lower in the tree overloading.'

Instead this should read: 'Sometimes we call this act of replacing attributes by redefining them lower in the tree overriding.'

Please see following definitions of 'overriding' and 'overloading' for justification of the above suggested change.

overriding
https://en.wikipedia.org/wiki/Method_overriding

overloading
https://en.wikipedia.org/wiki/Function_overloading

Function overloading refers to different implementations resulting from differently overloaded objects passed as parameters.

Method overriding on the other hand refers to different implementations arising from a class being a subclass of some superclass. I believe this is the meaning that the author wishes to ascribe in this paragraph.

Note from the Author or Editor:
[No change required, retained as informational only.]

(This is on Page 829 in printings after July, 2016.) Actually, this paragraph--like developers in general--uses _both_ terms to cover its bases. Here is its full text:

"""
Recall that inheritance searches proceed upward from instances to subclasses to superclasses, stopping at the first appearance of the attribute name that it finds. In this case, since the display name in SecondClass will be found before the one in FirstClass, we say that SecondClass _overrides_ FirstClass’s display. Sometimes we call this act of replacing attributes by redefining them lower in the tree _overloading_.
"""

As you can see, the equivalence of terminology is noted explicitly in the immediately-preceding sentence, and early in the text; it would be pointless and odd to change the second term to "overriding" given this context; and both "override" and "overload" are used frequently throughout the book. This post seems to be picking nits over terms that are no more formally differentiated in practice than "super class" and "base class"

On another point: nothing personal, but I'm also strongly disinclined to base anything in this book on the content of Wikipedia, which, after all, is hardly authoritative, often opinion-based, and sometimes just plain wrong. Despite current memes, crowdsourced web sites do not necessarily trump subject matter experts.

Ben VAN EVERY  Aug 04, 2016  Jan 06, 2017
PDF
Page 821
2nd paragraph, last sentence

"require" should be "required"

Note from the Author or Editor:
Agreed; change the first of the following to the second:

the require test and ...
the required test and ...

And good catch; it's always surprising to find lingering minor typos like this missed by so many proofers and readers.

Anonymous  Jul 29, 2015  Sep 04, 2015
Printed
Page 843
2nd paragraph

"When run directly, this module's self-test makes two instances and prints them; the __repr__ defined here shows the instance's class, and all its attributes names and values, in sorted attribute name order."

The word "attributes" should have an apostrophe: "...and all its attributes' names and values..."

Note from the Author or Editor:
Yes (of course!); change "attributes names" to "attributes' names" in line 2 of the referenced paragraph.

Jackson Coakley  Jul 20, 2015  Sep 04, 2015
Printed
Page 857
answer to question 8

"...the Person constructor would have change the job default to an empty list; and the Manager class would probably need to ..."

I think you may be meant:

"the Person constructor would have to change the job default to an empty list"

or possibly,

"the Person constructor would have changed the job default to an empty list"

Note from the Author or Editor:
Minor, but worth a patch: change referenced "would have change" to "would have to change".

Jackson Coakley  Jul 20, 2015  Sep 04, 2015
Printed
Page 857
answer to question 9

"A method named sendmail, for example, might use Python's standard library smptlib module to send an email to..."

You meant to write "smtplib," not "smptlib."

Note from the Author or Editor:
Yes; change "smptlib" to "smtplib". (Dyslexia is a terrible thing...)

Jackson Coakley  Jul 20, 2015  Sep 04, 2015
Printed
Page 857
para 3, lines 2/3

Near the end of line 2 in answer #8, change the first of the following to the second:
"; the Person constructor would have changed"
"; the Person constructor would have to change"
We changed this clause in the last reprint, but the edit needs to be edited.

Mark Lutz
 
Sep 08, 2015  Nov 25, 2015
Page 868
Python code example

The test code tries to call klass().method() but klass is the classes name, not the name of an instance variable. To fix I added c = klass() and replaced klass().method() by c.method() This may be a version creep issue, as I am using Python 3.9

Note from the Author or Editor:
[No edits required] No, this example is correct as is, if potentially confusing. Its klass().method() actually makes a temporary instance object along the way, and has to be read left-to-right, the same way that Python evaluates it.

1) First, klass() runs and returns a temporary result - a new instance object
2) Then, the new instance object's .method() is run as usual

The klass variable steps through a tuple of class objects (it's Inheritor, then Replacer, and so on), so the loop makes instances of each in turn. The instance object made by the klass() step (whose parenthesis mean call) lives in memory while this code runs, but is deleted as soon as the .method() part finishes.

So, the book's version is equivalent to yours, except that you've assigned the instance object to a variable c explicitly, so it will live on and be accessible after the method call via the variable. For this example's demo purposes, the instance need not be retained.

Study this again if this isn't clear. Expression evaluation like this has always been a fundamental property of Python that hasn't changed in later releases, and probably couldn't without breaking most programs ever written.

Anonymous  Sep 28, 2021  Jul 15, 2022
Page 875
2nd paragraph (LEGB)


name = 'global'

# del name

def func():
name = 'enclosing'
class Cls:
print(name) # 1st print
name = name # Cls.name attr
print(name) # 2nd print

# running func() outputs:
# 1st print => global
# 2nd print => global
#
# but uncommenting del name
# throws NameError
#
# Isn't name supposed to reference
# 'enclosing' from func's local scope,
# according to LEGB scope lookup protocol?
#
# python --version => 3.10.6
# Operating System => Ubuntu
func()

Note from the Author or Editor:
[No changes required; clarification/supplement only]

For this reply, please visit:

https://learning-python.com/lp5e-class-scope-bug-errata-mar24.html

This reply was relocated there because this errata page doesn't support larger replies, and breaks embedded code so badly that it cannot be read.

The TL;DR version is that this post stumbled onto an obscure bug in Python class scopes that's been known but unfixed open since 2010. The reply illustrates this bug's consequences.

(Future posters: please don't use this page as a chat forum. It's meant only for simple book errata posts, and cannot handle the sort of interaction afforded by other contacts. I'm happy to respond to more substantial queries by email.)

Anonymous  Mar 12, 2023 
Printed, PDF, ePub, Mobi, , Other Digital Version
Page 888
Very end of note near page bottom

Not really covered in this book, but the "mutable" should be "immutable" at the second-last word in this note (about __new__ use cases: it allows subclasses to set-up initial values for immutables if needed). Change:
"... instances of mutable types."
to:
"... instances of immutable types.".

Mark Lutz
 
Aug 01, 2013  Aug 16, 2013
Printed
Page 938-939
final example on p. 938, first example on p. 939

The hypothetical "reader" object in these examples uses inconsistent methods.

On p. 938:

def processor(reader, converter, writer):
.... while True:
.... .... data = reader.read()
.... .... if not data: break
.... .... data = converter(data)
.... .... writer.write(data)

On p. 939, in the OOP method-version equivalent of this function:

def process(self):
.... while True:
.... .... data = self.reader.readline()
.... .... if not data: break
.... .... data = self.converter(data)
.... .... self.writer.write(data)

In the function example, the "reader" object uses the method read(), but in the latter OOP example, it uses the method readline().

Note from the Author or Editor:
EDIT: at the very end of the first paragraph on page 939 that ends "one way to code the class:", add the following parenthesized clause just before the ":":

"(it also mutates one method name because we'll actually run this code)"

There appears to be ample room on this page to avoid re-paging.

DISCUSSION: This seems pretty trivial, given that the original (first) version copied here from page 793 was partial/skeleton code that was never run (abstract), and we're migrating here to actual code that is run (concrete). Changing the method names would require changing both here and on page 793, and seems unwarranted. The simple patch above is intended to avoid future confusion.

Jackson Coakley  Jul 20, 2015  Sep 04, 2015
PDF
Page 954
2nd paragraph

The following sentence: "Factories can be a major undertaking in a strongly typed language such as C++ but are almost trivial to implement in Python" seems to suggest that Python is not strongly typed, which causes confusion because on P149 of the book, you mentioned that Python is strongly typed. Could you clarify this point?

Note from the Author or Editor:
REPRINTS: Please change the "strongly" here to "statically". This is on page 986 in recent printings, near the end of the first paragraph. Specifically, change:
in a strongly typed language
to:
in a statically typed language
This shouldn't impact page breaks.

DISCUSSION: I agree with the poster - there's an important distinction between static and strong typing, as called out earlier in the book (e.g., pages 99/97 and 181/175). Python is strongly typed - it has specific rules about what you can do to each type, but it is not statically typed - it deduces object types dynamically from their creation, instead of program declarations. By contrast, C++ is both strongly and statically typed; it's static type declarations make it difficult to use objects with the same flexibility as in Python.

I suspect that prior editions of this text were not as crisp on the distinction as the current one is, and this is probably a lingering artifact of the earlier looseness. We'll tighten this up in the next reprint.

Anonymous  Mar 01, 2020  Jun 05, 2020
Printed, PDF, ePub, Mobi,
Page 960
second bullet point on page

"""
... the instance's memory address by calling the id built-function.....

"""

I believe it meant to read

""
...by calling the id built-in function
""

Note from the Author or Editor:
Yes -- change as noted: "id built-function" => "id built-in function".

Joseph Chandler  Jun 06, 2014  Aug 22, 2014
Printed
Page 960
last paragraph

"""
...echoes still use the default format because we're left __repr__ as an option for clients
"""

should read

"""
...format because we've left ...
"""

Note from the Author or Editor:
Yes -- change as noted: "we're left" => "we've left".

Joseph Chandler  Jun 06, 2014  Aug 22, 2014
ePub
Page 966
last few lines before "Listing attributes per object in class trees" section

Addressing the "Looping in __repr__" question, it is mentioned that "you can avoid the loops by using isinstance to compare the type of attribute values against types.MethodType in the standard library, to know which items to skip."

But this does not seem to work. Once you use getattr(self, attr) or its alternative (e.g., eval) to access the attribute values, you trigger __repr__, before doing comparison with types.MethodType or skipping any items. Therefore the loop is not avoided.

Would you mind clarifying this or posting the code for the updated __repr__ overloading?

Note from the Author or Editor:
[No book changes required: informational only]
Sure -- though the code formatting might not come out very well on this page. In short, you must run the type test _before_ the string formatting; getattr() doesn't trigger __repr__ itself, the string formatting does. Here's the __repr_ version without the loop:

# listinherited-reprfix.py (less __main__ code and comments trimmed)
import types
class ListInherited:
"""
Use dir() to collect both instance attrs and names inherited from
its classes; use __repr__, but avoid loops for methods by type testing;
"""
def __attrnames(self):
result = ''
for attr in dir(self):
if attr[:2] == '__' and attr[-2:] == '__':
result += '\t%s\n' % attr
else:
obj = getattr(self, attr)
if isinstance(obj, types.MethodType):
result += '\t%s=<<Method: %s>>\n' % (attr, obj.__name__)
else:
result += '\t%s=%s\n' % (attr, obj)
return result

def __repr__(self):
return '<Instance of %s, address %s:\n%s>' % (
self.__class__.__name__,
id(self),
self.__attrnames())

This works the same as the __str__ based listinherited.py in the book, but doesn't display the class to avoid the loop (displaying additional detail is left as exercise):

c:\code> listinherited.py
<Instance of Sub, address 43359144:
_ListInherited__attrnames=<bound method Sub.__attrnames ...
...__X names trimmed
data1=spam
data2=eggs
data3=42
ham=<bound method Sub.ham of <testmixin.tester.<locals>.Sub ...
spam=<bound method Sub.spam of <testmixin.tester.<locals>.Sub ...
>

c:\code> listinherited-reprfix.py
<Instance of Sub, address 43549640:
_ListInherited__attrnames=<<Method: __attrnames>>
...__X names trimmed
data1=spam
data2=eggs
data3=42
ham=<<Method: ham>>
spam=<<Method: spam>>
>

Folow-ups to lutz@rmi.net.

Shaopeng  Sep 03, 2014 
Printed
Page 970
Second sentence of the last full paragraph

The sentence reads "The processing code only cares...that a method named convert is defined." It should read " that a method named converter is defined" as the code listing on the prior page defines an abstract superclass with a method named Processor.converter.

Note from the Author or Editor:
Yes--please change the referenced:
a method named convert
to:
a method named converter
using literal font for all of "converter" only.

A minor typo, but worth the patch. This sentence correctly used "converter" in the 2nd Edition; was for reasons now unknown changed to the incorrect "convert" in the 3rd; and has remained unnoticed in the 4th and 5th until this post. In other words: good catch. [This is on page 940 in earlier printings.]

Jason Gastelum  Jan 16, 2019  Mar 22, 2019
Page 974
Version skew note, 2nd paragraph

I wanted to ask just for clarification, if the phrase "Printing a wrapped object directly (...) which then passes the call on to the wrapped object." shouldn't have been "Printing a *wrapper* object directly...". It would help for a better understanding of the "version skew" and the overall delegation behavior. Thank you. C.S.

Note from the Author or Editor:
[No edits required]. I understand the confusion here, and partly agree with this post, but the original wording seems to convey the intent slightly better than the suggested change. A "wrapped" object is indeed a "wrapper" object in memory once it's wrapped, but it's not generally thought of that way. The object has simply been augmented with a wrapper layer, which routes calls back to the original "wrapped" object, which is the true subject of operations. Calling out the "wrapper" object as proposed seems to give it too much prominence in the model. Grey to be sure, but I'd rather leave this as it is. I'm keeping this reply here as a clarification for others.

Carlos Sims  Apr 20, 2022  Jul 15, 2022
Printed, PDF, ePub, Mobi,
Page 977, 1235
Two new footnotes

Assuming we can fit them in without impacting page breaks badly, I'd like to add two footnotes about use of the __dir__ method in dynamic or proxy classes based on __getattr__ or __getattribute__.

This operator overloading method (among others) is not covered in this book for space, but there are two ideal places to add a mention, plus a pointer to additional coverage in the latest Python Pocket Reference.

THE CHANGES:

1) On Page 977, add a footnote reference at the end of the first paragraph on this page:
"""
...ethereal at best!*
"''"

which refers to the following new footnote text:
"""
* Some dynamic and proxy objects based on __getattr__and the like can also use the __dir__ operator overloading method to manually publish an attributes list for dir calls. Because this is optional, though, general tools cannot rely on their client classes to do so. See other resources such as Python Pocket Reference, 5th Edition for more on the __dir__ method.
''""

in which __dir__, __getattr__, and dir are literal font, and the book title is italics and possibly a link (this is the end of chapter, so it's okay if this changes the last page break, but if this adds a page, we can shorten or drop the last sentence of the insert, or simply cut its "other resources such as" part if sufficient).

2) On Page 1235, add a footnote reference to the end of the first sentence in the last paragraph:
"""
...names do not appear in dir results.*
"""

which refers to this new footnote text:
"""
* As noted in Chapter 31, such dynamic classes can also use a __dir__ method to provide an attribute result list for dir calls, though general tools cannot depend on this optional interface.
""""

in which __dir__ and dir are literal font (this page appears to have enough space, but again, let me know if it changes paging substantially).

Mark Lutz
 
Jan 12, 2014  Jan 24, 2014
Printed, PDF, ePub, Mobi,
Page 977
1st paragraph

The text reads:

"Tools that attempt to display data in a wildly dynamic language Python must come with the caveat that some data is etheral at best!"

And should probably read:

"Tools that attempt to display data in a wildly dynamic language like Python must come with the caveat that some data is etheral at best!"


Note from the Author or Editor:
Yes--please insert the "like" as suggested.

(It could be argued that the sentence parses either way, but the "like" was the intended and better phrasing--a typo that somehow went unnoticed, despite being read at least 50 times by its author, not to mention editors and proofers.)

Anonymous  Feb 21, 2014  May 02, 2014
Printed, PDF, ePub, Mobi, , Other Digital Version
Page 982
bottom code listing (setsubclass.py, line #4)

ORIGINAL READER POST (misfiled against 3rd Ed, moved here by author):
"""
On line 5 of code listing, inside the __init__ function, this line appears:

list.__init__([]) # Customizes list

Presumably this is meant to call the parent __init__ method to initialize the Set instance. It is however dead code, since it initializes the empty list given in the argument instead. The correct code is like this:

list.__init__(self, []) # The second argument is not really needed

Another minor gripe is the use of a mutable default for the value argument to __init__. This may lead to subtle bugs in future versions if the list referred to by value is somehow stored or returned so that the user can get at it. [...plus an editorial remark on how this reflects "bad coding practice" pruned here...]
"""

Note from the Author or Editor:
Reprints: this merits a single patch -- please change the referenced line to the following, only replacing "[]" with "self" (retaining the #'s vertical alignment):

list.__init__(self) # Customizes list

Discussion: The post raised two different points: the __init__ call (valid), and the "value" mutable argument (invalid).

POINT 1) Yes -- the list.__init__ line as shown in the book is harmless and not a bug per se, but it is coded out of idiom, and is pointless code. In short, because list.__new__ initializes the list to be empty before list.__init__ is ever invoked, there is no need to call the latter. The ideal coding is to omit this line altogether, but if used it should pass "self" as its first argument for consistency.

Per the C implementation of the list type (file Objects/listobject.c of Python's source code), list.__new__ creates a new, empty list instance object; a later call to list.__init__ simply clears the 1st argument (called "self", but required only to be any list) to be empty if it isn't already, and then extends it with the items in a sequence if one is passed in as a 2nd argument.

Hence, passing in just "[]" for the 1st argument to list.__init__ does nothing: the passed empty list is already empty, and no 2nd argument is appended to it. But the same will be true if we pass in just "self" as the 1st argument as prescribed, because it's already an empty list after list.__new__ has run. That is, there is no reason to also call list.__init__ from its redefinition to reinitialize to an empty list. On the other hand, this example also aims to reinforce the general pattern of calling redefined constructors, and this code must still call its own self.concat to extend without duplicates (this is a set implementation).

Interestingly, this code line has been present in the book since the 2nd Edition (of 2003--10 years ago), and has gone unremarked by hundreds of thousands of readers until now. It's on Page 982 of the 5th Edition, 778 of the 4th, 542 of the 3rd, and 365 (?) of the 2nd, before which types could not be subclassed.

POINT 2) No -- the mutable nature of the default "value" argument is neither relevant nor problematic here. This class simply scans the value's individual items in self.concat, and never stores, changes, or returns the value object itself as a whole. In the process, it effectively creates a copy that is immune to mutable side-effects. Please reread the code, and see earlier in the book (e.g. much of Chapter 6, the latter portions of Chapter 9, and Page 658) for coverage of this key core concept.

Mark Lutz
 
Aug 01, 2013  Aug 16, 2013
Printed
Page 984
last block of output

In this section, the author is demonstrating that a class is a callable object. In the last step of the sample code, we create a dict of callables keyed by their result, including a Negate instance and the class itself. Then we iterate through the key, value pairs of the dict, printing result strings using the str format method: print('{0:2} => {1}'.format(key, value))

However, on the line above, the Negate instance throws the following exception: "TypeError unsupported format string passed to Negate.__format__". As per https://docs.python.org/3/reference/datamodel.html, presumably this is because the interpretation of the format_spec argument is up to the type implementing __format__(),

In order to format Negate, overload Negate.__format__ as follows to delegate to the __format__ method of self.val's type:

def __format__(self, spec):
return self.val.__format__(spec)

Note from the Author or Editor:
[No changes required - informational only] This example works as shown, but the language tools it relies on have unfortunately changed -- and multiple times -- in Python 3.X since this book was published, and it's impossible to cover these changes in the book at this point. Per testing:

1) This example works correctly, without error, and as shown in the book in both Python 3.3 and 2.7, the versions current at publication and explicitly noted as covered by the text. In both of these Pythons, the Negate class's output line is as the book lists it: "-5 => <class '__main__.Negate'>" .

2) In Python 3.5, though, the formatted Negate class generates an exception, with text: "TypeError: non-empty format string passed to object.__format__". 3.4 was unavailable for testing, but Python changes broke this code by 3.5.

3) Worse, in Pythons 3.6 and 3.7, the Negate class formatting also fails, but with different exception text that is the same as that reported by this post: "TypeError unsupported format string passed to Negate.__format__".

In other words, the format() method's machinery has been the scene of multiple thrashings since the book was published. It changed some time around Python 3.5, and then again in 3.6, and both changes left the book example broken for users of these Pythons. This is just one minor example in a large book, but it's less than ideal when unchanged code breaks -- twice.

There's not much that the book can do about this today, but I'm retaining this reply for other readers who may encounter this in recent Python releases. Given Python's constant and rapid rate of change, readers should expect to consult Python's latest docs, especially its What's New, for recent Python gyrations -- exactly as this poster did.

[Changed to clarification given the context; this is on page 953 in earlier printings.]

Jason  Jan 13, 2019  Mar 22, 2019
Printed
Page 986
Second sentence of third paragraph

The sentence reads "It expects to be passed a class object... with one or more arguments for the class's constructor." But it should read "...zero or more arguments...".

The code listing above even demonstrates an example of a factory function passed a class object with zero arguments.

Note from the Author or Editor:
Sure; this seems a bit picky and trivial, and is made clear by the examples that surround this text (and lots of coverage of * and ** in the functions chapter), but it's worth a quick patch here. Change the referenced:
one or more arguments
to:
zero or more arguments
[This is on page 955 in earlier printings.]

Jason Gastelum  Jan 16, 2019  Mar 22, 2019
Printed
Page 990
1st paragraph

"In a more realistic delegation scenario, this means that built-in operations like expressions no longer work the same as their traditional direct-call equivalent."

For symmetry, the words "operation" and "equivalent" should either both be plural or both be singular:

"In a more realistic delegation scenario, this means that built-in operations like expressions no longer work the same as their traditional direct-call equivalents."

"In a more realistic delegation scenario, this means that a built-in operation like expressions no longer work the same as its traditional direct-call equivalent."

Note from the Author or Editor:
Granted; let's make these agree as plurals. On page 990, change the end of the first sentence of the first paragraph to "direct-call equivalents." (adding just "s").

Jackson Coakley  Jul 20, 2015  Sep 04, 2015
Printed
Page 997
1st paragraph under "Diamond Inheritance Change" heading

"...and whose name comes from the diamond shape of the tree if you sketch out-- a square resting on one of its corners."

Maybe "...if you sketch it out"?

Note from the Author or Editor:
Yes: change to "sketch it out".

Jackson Coakley  Jul 20, 2015  Sep 04, 2015
Printed, ePub
Page 998
Second paragraph of "Implications for diamond inheritance trees" section, line #3

The line says "... would visit x, D, B, A, C, and then A." Why it will visit A again at the end? I think "and then A" should be deleted.

Note from the Author or Editor:
[No changes required; retained as informational only.]

(This is on page 1033 in printings after July, 2016.) Your point is well-taken, but this referring to the complete route through the class tree for classic classes (as the book says, "the full DFLR search order"). In such classes, a common ancestor in diamonds can be visited more than once if an attribute has not been found. If an attribute is found along the way, the search end immediately--in this case, after visiting x, D, B, and A, as described in the text; C also won't be visited in this case. New-style classes search siblings before parents, and avoid ever visiting a common ancestor more than once--search success or not.

Anonymous  Jun 13, 2016  Jan 06, 2017
Page 1013
The last code snippet on Page 1013 (#Less wrong... part)

For the following code on Page 1013

for attr in list(getattr(X, '__dict__', [])) + getattr(X, '__slots__', []):
print(attr, '=>', getattr(X, attr))

If the instant object doesn't assign some names defined in the __slots__ sequence, the above code would return an AttributeError, for example, running the above code in the following case would cause X.b to fail:
class D(object):
__slots__ = ['a', 'b', '__dict__']
X = D()
X.a, X.c = 1, 3

Maybe provide a default value for getattr() to make the code a little more robust:

for attr in list(getattr(X, '__dict__', [])) + getattr(X, '__slots__', []):
print(attr, '=>', getattr(X, attr, 'NOT ASSIGNED'))

Thank you!

Note from the Author or Editor:
[No edits required]. You're right that the code would fail if 'X.b' is not assigned, but that's intentionally illustrated earlier in the session by the failing 'X.a' fetch, and 'X.b' is assigned before this code is run to avoid the error.

Really, the point of this example is to demo some of the strange boundary cases of slots - one of which you uncovered. It's also true that the code would be more robust with a getattr() default as you noted, but this is supposed to show slot oddities like this, not be production-grade code. So, I'd rather not change this specific example, but I'll keep this post for any others who may stumble onto the surprise.

Yi Ren  Jul 10, 2021  Jul 15, 2022
PDF
Page 1019
slots-test.py file

The benchmarking results in Python 2.X, were pretty much identical because it seems you forgot to inherit from (object) in class C, and so __slots__ was probably ignored. Just change "class C:" to "class C(object):" the two times they are found so they become new-style classes.

Note from the Author or Editor:
Yes, and great catch - the "(object)" requirement for slots in 2.X is spelled out in this section, but neglected in the timing script itself, which was added late and in haste, unfortunately. With the correction, 2.X shows the same slots performance pattern as 3.X. This is a minor issue in a minor example for an obscure feature, but worth fixing in the book nonetheless.

On page 1019:

1) Change both of the two occurrences of "class C:" to "class C(object):" in the first code listing (lines 13 and 19).

2) Change the text in line 2 of paragraph 2 from "slightly quicker in 3.X and a wash in 2.X" to "slightly quicker in both 3.X and 2.X".

3) Change lines 4 through 6 of this page's second code listing from the first of the following to the second, keeping their original formatting (and their "=>" must align vertically):

c:\code> py −2 slots-test.py
Slots => 0.80868754371
Nonslots=> 0.802224740747

C:\code> py -2 slots-test.py
Slots => 0.615521153591
Nonslots=> 0.766582559582

Anonymous  Feb 09, 2016  Sep 02, 2016
Printed
Page 1039
2nd paragraph, between examples

"In fact, as coded, this decorator can be applied to class or functions--..."

"class" should be plural here: "...classes or functions--..."

Note from the Author or Editor:
Yes, change "class" to "classes". (Read by many, noticed by none till now; see other comment regarding brains and missing letters).

Jackson Coakley  Jul 20, 2015  Sep 04, 2015
PDF, ePub, Mobi
Page 1040
4th paragraph

Really minor, but anyway, the text reads

"Such mind-binding concepts will require..."

Was that meant to say:

"Such mind-bending concepts will require..." ?

Note from the Author or Editor:
Yep -- change to "bending". (It works the other way too, and "binding" may be arguably funnier, but it was supposed to say "bending".)

Anonymous  Mar 13, 2014  May 02, 2014
Printed
Page 1049
Penultimate paragraph

"Superclass that might be changed at runtime dynamically preclude hardcoding their names in a subclass's methods..."

"Superclass" should be plural: "Superclasses that might be changed at runtime dynamically..."

Note from the Author or Editor:
Yes, change just "Superclass" to "Superclasses" at the start of the last paragraph on this page. (Brains fill in missing letters more readily than they probably should.)

Jackson Coakley  Jul 20, 2015  Sep 04, 2015
Printed
Page 1050, 1053
Final paragraph p. 1050, 1st paragraph p. 1053

"In this mode, each super call selects the method from a next class following it in the MRO ordering of the class of the self subject of a method call." p. 1050

"By selecting a next class in the MRO sequence, a super call in a class's method propagates the call through the tree..." p. 1053

Perhaps consider changing the phrase "a next class" to "the next class".

Note from the Author or Editor:
EDIT: The suggested change is not quite valid for subtle reasons given below. But let's add a minor insert to avoid future confusion. In page 1050's last paragraph, add a new sentence immediately following the first sentence, which ends in "of a method call." The new sentence should read as follows (and we seem to have plenty of space for it):

"This selection process chooses the first class following the calling class having a requested attribute."

DISCUSSION: The original post is invalid by itself -- "a" was used on purpose here, because "the" next class may not have the attribute/method being accessed. If so, super() skips ahead through successive followers on the MRO until the attribute is found. Section "Coupling: Application to mix-in classes" ahead on page 1057 describes and demonstrates this in depth.

Hence, "the" implies the next class and no other, which is not always correct; "a" is used instead to suggest that the selected class may be further ahead on the MRO chain. The search ahead might have been called out more explicitly in the early sections here (the patch above seeks to do so), and "the" is used when examples require no search; but "the" would be misleading in the referenced context.

Jackson Coakley  Jul 20, 2015  Sep 04, 2015
Printed, PDF, ePub, Mobi,
Page 1064, 1386
Adding a new notebox and backreference

Assuming we can fit it in without impacting page breaks badly, I wish to add a note clarifying how super() and inheritance relate. The short story is that they don't: super() precludes normal full inheritance, and instead performs a custom scan. There's more on this in the latest Python Pocket Reference (along with other fine points omitted from LP5E for space), but a few words may help here too.

THE CHANGES:

1) On Page 1064, just before header "Class Gotchas", add a new Note box with the following text (in which "super" is literal font, "Chapter 40" is a link, and "--" is a single dash character; this will shift some page breaks, but it's near the end of chapter, and the breaks from here to chapter end are arbitrary; let me know if it adds a page):

"""
Also watch for Chapter 40's formal description of full inheritance -- a procedure which super objects eschew for a custom scan of a context-specific MRO tail, looking for the first appearance of an attribute (descriptor or value) along the way. Full inheritance is used on the super object itself only if this scan fails. The net effect is a special case for basic name resolution, imposed on both the language and your code for the sake of a relatively rare use case.
"""

2) On Page 1386, at the end of paragraph 2, change the sentence just before header "Assignment inheritance" that reads:
"""
See Chapter 38 for more on these tools and descriptors.
"""
to the following, adding just the clause at the end, in which "super" is literal font, and "Chapter 32" is a link:
"""
See Chapter 38 for more on these tools and descriptors, and Chapter 32 for the super
special-case MRO scan.
"""
I'm hoping this doesn't add a line and change page breaks; let me know if it does,

Mark Lutz
 
Jan 12, 2014  Jan 24, 2014
Printed
Page 1097
Final paragraph before Version Skew Note

"This form has most of the same convenience of the *empty* except, without the risk of catching exit events."

In the above, the word "empty" is in bold and the word "except" is not. This is backward; the word "except" should be in bold and "empty" should not.

Note from the Author or Editor:
Yes, please change as noted: "except" should be bold/literal font, and "empty" should be normal. We got this one right 7 out of 8 times in this chapter.

Jackson Coakley  Jul 20, 2015  Sep 04, 2015
Printed
Page 1104
first code listing

The code listing sets the __str__ attribute of Sub to Lister.__str__. Setting it to ListTree.__str__ would be more clear, as the current listing implies that the lister module from Chapter 31 has been imported; but no such assumption is required if we simply reference ListTree.__str__.

Note from the Author or Editor:
This code assumes the lister.py collector/selector module back on page 1006, but I agree that it's a bit of a stretch given the lack of an explicit reference or import here (probably, this example code was nearer to the required import originally).

[EDIT 1] Let's change the "Lister.__str__" here to "ListTree.__str__" as proposed, but we also then need to change it in the "#" comment to the right. The updated code line should be this, making sure its "#" aligns with that of the preceding line as before:

__str__ = ListTree.__str__ # Explicitly pick ListTree.__str__

[EDIT 2] Also, while researching this report, a related typo turned up. On page 995, paragraph -2, please change the "ListerInstance" to "ListInstance". (I don't know the history on this one either, but it's probably just one too many listers to keep straight.)

Jason Gastelum  Jun 02, 2019  Nov 22, 2019
Printed
Page 1111
2nd paragraph

"...the causality chain can be arbitrary long, and is displayed in full in error messages."

Perhaps you meant to write "can be arbitrarily long" or "can be of arbitrary length".

Note from the Author or Editor:
Minor but true: change "can be arbitrary long" to "can be arbitrarily long", retaining the existing italics.

Jackson Coakley  Jul 20, 2015  Sep 04, 2015
Printed
Page 1113
Penultimate paragraph

"Of course, there are exceptions for most rules--..."

Should instead be:

"Of course, there are exceptions to most rules--..."

Note from the Author or Editor:
Sure -- change as suggested: "for" => "to" (perhaps a bit nit-picky, but marginally better).

Jackson Coakley  Jul 20, 2015  Sep 04, 2015
PDF
Page 1156
2nd paragraph

functional programing tools...

Note from the Author or Editor:
Yes -- change to "programming". MS-Word's spellchecker says it can be spelled either way (which is likely why this went unnoticed), but we use the double-"m" form everywhere else.

Anonymous  Aug 20, 2013  Nov 08, 2013
Printed, PDF, ePub
Page 1169
5th paragraph

The text reads:

"But all of these encoding schemes -- ASCII, Latin-1, UTF-8, and many others -- are considered to be Unicode."

Perhaps it should be clarified that though the ASCII and Latin-1 **character sets** are both considered Unicode (are proper subsets of Unicode), only ASCII and UTF-8 **encodings** would pass as "Unicode encodings" (a Latin-1 encoding would encode characters (128-255) as single bytes with bit patterns outside of those accepted by UTF-8, and UTF-16 and UTF-32 also - naturally).

Note from the Author or Editor:
No change required on this, but I'm retaining it as a confirmed author note in case it may prove useful context to other readers.

To me, this seems to be splitting hairs over very subtle shades of meaning, and the proposed rewording doesn't offer much over the original. The main and simple point here was that all three fall under the Unicode umbrella. Tightening this text up to distinguish character sets and encodings would seem to distract too much from its simpler goal, but your milage may vary.

Anonymous  Mar 19, 2014 
Printed
Page 1170
Paragraph starting with "Python 3.3's new scheme..."

"...repeating a single ASCII letter and getting a substring of an ASCII strings is 4 times faster."

"ASCII strings" should be singular: "...a substring of an ASCII string is 4 times faster."

Note from the Author or Editor:
Yes (another minor typo read by many and noticed by none); change the clause in line 5 of this paragraph to: "an ASCII string is 4 times faster;".

Jackson Coakley  Jul 20, 2015  Sep 04, 2015
Printed
Page 1180
First line

To match the code listing below, change "0xCD" to "0xC4"

Note from the Author or Editor:
[On page 1220 para 5 in printings after Sep-2016]
Yes: a minor but valid typo. Please change "For instance, the hex values 0xCD and 0xE8" to "For instance, the hex values 0xC4 and 0xE8", using "0xC4" to match the code following this.

Nick Reeder  Oct 29, 2016  Jan 06, 2017
Printed
Page 1180
First sentence of section named Encoding and Decoding Non-ASCII text

Change "raw bytes using as ASCII" to either "raw bytes using ASCII" or "raw bytes as ASCII"

Note from the Author or Editor:
[On page 1221 para 2 in printings on or after Sep-2016]
Yes: a valid typo. Please change "into raw bytes using as ASCII," to "into raw bytes as ASCII," dropping the fully-superfluous "using", and let me know is this changes page-breaks here (we can expand if needed).

Nick Reeder  Oct 29, 2016  Jan 06, 2017
PDF
Page 1183
2nd sentence of 2nd paragraph

Change 'str stings' to 'str strings'.

Note from the Author or Editor:
Yes: please change "str stings" to "str strings" as suggested (insert joke about being stung by strings here). This is on line 2 of page 1224 in the latest printings (on or after Sep-2016).

zhenguoli  Feb 17, 2017  Apr 21, 2017
Printed, PDF, ePub
Page 1206, 1217/1218, 1243
as described in the 6 edits noted

In hindsight, the book isn't as crisp as it might be on default Unicode encodings. This is a subtle issue that varies per platform and Python line, and is impossible to cover in full now; but the following 6 minor patches will improve the existing material, and shouldn't change page breaks much (please let me know if they do, and run the edits past me for review):

1) ON PAGE 1206, near the end of the third bullet item
--change--
(e.g., ASCII, or UTF-8 on Windows in the U.S.—sys.getdefaultencoding gives your default if you care to check),
--to--
(e.g., ASCII, UTF-8, or Latin-1—locale.getpreferredencoding(False) gives your open default if you care to check),
--where--
The "locale.get...(False)" part and "open" are literal font.
This is on page 1166 in older printings.

2) ON PAGE 1217, the entire last paragraph
--change--
These encode and decode methods (as well as file objects, described in the next section) use either a default encoding for your platform or an explicitly passed-in encoding name. For example, in Python 3.X:
--to--
Both these encode and decode methods and the file open calls we'll explore ahead use either an explicitly passed-in encoding name or a default. In Python 3.X, the methods' default is always UTF-8, but open uses a value in the locale module that may vary per platform. In 2.X both defaults are usually ASCII, as exposed in the sys module (which allows changes at start-up). For example, in 3.X:
--where--
All "encode", "decode", "open", "locale", and "sys" are literal font.
This may add a page break; to localize the change, either let the paragraph run over to the next page, or move just its last sentence to the next page.
This is on page 1177 in older printings.

3) ON PAGE 1218, paragraph 1, sentence 2
--change--
First of all, your platform’s default encoding is available in the sys module,
--to--
First of all, your platform’s various default encodings are available in the sys and locale modules,
--where--
The "sys" and "locale" are literal font.
This is on page 1177 in older printings.

4) ON PAGE 1218, second code listing, lines 1 through 5
--change--
>>> import sys
>>> sys.platform # Underlying platform
'win32'
>>> sys.getdefaultencoding() # Default encoding for str here
'utf-8'
--to--
>>> import sys, locale # Windows open() uses cp1252 (Latin-1)
>>> sys.platform # but str() never uses a default...
'win32'
>>> locale.getpreferredencoding(False), sys.getdefaultencoding()
('cp1252', 'utf-8')
--where--
All the original's font style and "#" vertical alignment is retained.
This is on page 1178 in older printings.

5) ON PAGE 1243, in the first sentence at the top of page
--change--
(and UTF-8 is Python 3.X’s default encoding):
--to--
(and Latin-1, a.k.a. cp1252, is Python 3.X’s default open encoding on Windows per locale.getpreferredencoding):
--where--
The "open" and the "locale.get..." part are literal font.
This is on page 1202 in older printings.

6) ON PAGE 1243, first code listing, lines 2 through 4
--change--
>>> import sys
>>> sys.getdefaultencoding()
'utf-8'
--to--
>>> import sys, locale
>>> locale.getpreferredencoding(False)
'cp1252'
--where--
All the original's font style is retained.
This is on page 1202 in older printings.

[Discussion only follows]
Looking over the output of the code on page 1243, the UTF-8 result of sys.getdefaultencoding() is clearly not used for open() on Windows. In the second code listing, the default encoding's result differs from an explicit UTF-8, and applies Latin-1 (a.k.a. cp1252) instead. The sys module does host some Unicode defaults, but the open() default must reside elsewhere.

This is a complex issue that varies per platform and Python line. In brief: per its recent docs, Python 3.X now uses the locale.getpreferredencoding(False) result's "guess" for the open() default. It seems to have meant to remove 2.X's sys.getdefaultencoding() but never did, leaving the matter tersely documented and confused. In 3.X, the sys module's setting still reflects the encoding methods' default, but open() doesn't use it; 2.X's policies vary, and include an odd removal of sys.setdefaultencoding() after startup.

This book obviously doesn't have space to cover this with full precision today; locales are a substantial topic alone, and the "guess" term in recent 3.X docs seems murky at best (and cautionary at worst; a code excursion seems required). But as a partial fix, the edits described can at least clarify the issue.

Of course, you should use explicit Unicode encoding names whenever possible and not rely on defaults that may vary per Python, platform, and machine. If you work across multiple platforms, using 3.X's open() defaults means that you'll have to remember where each file was last saved to reopen it correctly. Still, it's useful to know what those defaults may be, especially if you work in a single Unicode context.

Also note that the edits deal mainly with Python 3.X; users of Python 2.X's similar codecs.open() should consult 2.X manuals for more details (which, unfortunately, appear more terse than 3.X's!).

Mark Lutz
 
Mar 25, 2018  May 04, 2018
PDF
Page 1214, 1242, 1559, 1560
various

The book correctly defines the Unicode BOM as a "byte order mark" in two places (pages 1213 and 1236), but also incorrectly defines it as a "byte order marker" in two other places, plus two more in the index. This is a minor typo, and the terms are often used interchangeably outside this book too, but we should change all four "byte order marker" to use the more formally correct "byte order mark". (In printings before August, 2016, the impacted page numbers are 1174, 1201, and 1509.)

Mark Lutz
 
Aug 24, 2016  Sep 02, 2016
Printed, PDF
Page 1218, 1243
(see description)

Two Unicode-encoding edits for clarity:

1) On page 1218, at the end of line 1 in the second code listing, change:
>>> import sys, locale # Windows open() uses cp1252 (Latin-1)
to:
>>> import sys, locale # Windows open() uses cp1252 (a Latin-1 superset)
retaining the original "#" alignment.

2) On page 1243, in the first line on the page, change:
(or Latin-1, a.k.a. cp1252,
to:
(or Latin-1's cp1252 superset,

[Discussion: this may seem trivial, but the CP-1252/Latin-1 distinction is a fairly big deal; they're not the same, and treating them as such can lead to display issues (or worse). For a prime example, see:

learning-python.com/post-release-updates.html#showcodeunicode

In brief, CP-1252 has extra characters (or different characters, depending on the Latin-1 definition) which may be botched if treated as Latin-1. A slanted quote, for example, exists in CP-1252's character set, but not Latin-1's (its encoded byte may decode to an obscure control code in Latin-1). Many tools treat Latin-1 as CP-1252, since the latter is generally a superset of the former. But their decoded text may differ in your programs.]

Mark Lutz
 
Sep 03, 2018  Oct 12, 2018
Printed
Page 1227
Paragraph starting with "Descriptors with __set__ methods..."

"In short, a descriptor with a __set__ is known formally as data descriptor, and is given..."

Perhaps consider changing "is known formally as data descriptor" to "is known formally as a data descriptor."

Note from the Author or Editor:
Yes, change as suggested: "as data descriptor" => "as a data descriptor", with only "data descriptor" still in italics.

(This is pretty minor, and one could argue that this sentence reads fine as is if data descriptor is taken as a formal category, but the "a" reads better. Things like this slip by, despite paying for copy edits and proofreads.)

Jackson Coakley  Jul 20, 2015  Sep 04, 2015
Printed, PDF
Page 1228, 1229, 1224
(see description)

Three source-encoding edits for clarity:

1) On page 1228, middle of 3rd paragraph, --change--:
, Python uses the UTF-8 encoding by default, but it allows you to change this to support arbitrary character sets by including a comment that names your desired encoding. The comment must be of this form and must
--to--:
, Python uses UTF-8 in 3.X (and ASCII in 2.X) as its default encoding, but allows you to use arbitrary encodings and the character sets they support by including a comment that names your desired encoding. The comment is usually of this form and must

2) On page 1229, line 1 of 2nd paragraph, --change--:
fall back on the standard UTF-8 encoding,
--to--:
fall back on the default source encodings,

3) On page 1224, near end of first paragraph on page, --change--:
which, as discussed later, defaults to UTF-8 unless an encoding
declaration is given
--to--:
which, as discussed later, defaults to UTF-8 in 3.X (and ASCII in 2.X)
unless an encoding declaration is given

Some of these will probably change page breaks: please run past me if so.

[Discussion: as originally written, this section has two shortcomings: it did not note 2.X's different source-encoding default, and was too rigid on encoding comment format (Python actually accepts a few forms, though the one shown is by far most common). The 2.X default matters if you're writing code that must work on either line (see many ISPs). Edit #3 isn't required per se, given that it's in a 3.X code section, but it's best to restate the difference.]

Mark Lutz
 
Sep 03, 2018  Oct 12, 2018
PDF, Mobi
Page 1238
4th paragraph

In the description of the __setattr__ method:

"...an operator overloading method run for every attribute fetch,..."

Did you mean to say:

"...an operator overloading method run for every attribute assignment,..."

Note from the Author or Editor:
Yes -- change as described. (Likely obvious to most readers, given the many surrounding descriptions and examples, and "fetch" can be interpreted broadly; but "assignment" was clearly the intended and proper word in this clause.)

Anonymous  Mar 20, 2014  May 02, 2014
Printed
Page 1238
2nd paragraph

"These two methods are representatives of a set of attribute interception methods that also includes __setattr__ and __delattr_."

"__delattr_" should be "__delattr__" (two underscores on each side).

Note from the Author or Editor:
Yes, please change as suggested: "__delattr_" => "__delattr__". In our defense, this term shows up 12 times correctly with double underscores; no idea how this one fell through the cracks.

Jackson Coakley  Jul 20, 2015  Sep 04, 2015
Printed, PDF, ePub, Mobi,
Page 1250
code line 11

return lambda *args: '[Getattr str]'

It seems that "*args" here is unnecessary under all circumstances because object.__str__(self) expect 0 arguments.

Note from the Author or Editor:
No -- I'm not going to mark this as an errata, because the goal here was to simply flag interceptions of attribute fetches for built-in operations, not to provide full implementations for the intercepted operations. Hence, the intentional coding of a generic "any argument list goes" function with a *args is fine; it simply catches the call so as to produce an indicator message. This was also coded for symmetry with the result for other operations, whose arguments do vary.

That being said, this underscore an interesting point. The "*args" is indeed optional in this lambda, but for reasons more subtle than the post may have meant to imply. A __str__ implementation run by some prints should normally allow for at least the automatic "self" argument:

>>> help(object.__str__) # in 3.X
Help on wrapper_descriptor:

__str__(...)
x.__str__() <==> str(x)

>>> object.__str__()
TypeError: descriptor '__str__' of 'object' object needs an argument

Importantly, in this book example, __getattr__ is intercepting the attribute *fetch*, not the later operation *call*. By contrast, the explicitly coded __len__ in this class catches the operation call, not the attribute fetch. That is, the book example deliberately handles this step only:

>>> object.__str__
<slot wrapper '__str__' of 'object' objects>

In general, though, a program that really intends to implement __str__ in full should usually return a *bound method* object that retains the "self" available at attribute fetch time, and not a simple function created by a lambda. When Python later calls the fetch's result, it omits the original "self" from the arguments list, assuming it to be part of and provided by a bound method (or other state retention device) if needed by the operation implementation. Thus, no self argument is required of or passed to a simple function fetch result.

Python normally creates bound methods automatically on attribute fetch, when one is coded explicitly as a method with a self argument (like the class's __len__), or fetched from a proxied object (by calling the built-in getattr(wrapped, attrname), the normal coding pattern in delegation contexts). By using a lambda in the example, we're implying that the later operation call won't require the self object that was present at attribute fetch time; this simple call tracer does not.

Again, though, illustrating all this was well beyond the goals of this particular code. Its liberal method coding suffices as a catch-all demo of attribute fetch interception in __getttr__ and __getattribute__ -- and the lack thereof for built-in implicit fetches in new-style classes. See Chapter 31 for more on bound methods in general.

Yang Lifu  Aug 22, 2013 
PDF, Mobi
Page 1256
2nd paragraph

The paragraph starts:

"That short story here is..."

and should probably read:

"The short story here is..."

Note from the Author or Editor:
Yes -- change as described. (A minor typo that was missed by all.)

Anonymous  Mar 21, 2014  May 02, 2014
PDF, Mobi
Page 1273
4th paragraph

The text reads:

"...where decorator is a one-argument callable object that returns a callable object with the same number of arguments as F (in not F itself):"

The parenthetical note seems meant to have read:

"...(not F itself):"

OR

"...(not necessarily F itself):"

Note from the Author or Editor:
The parenthesized bit should say:

"(if not F itself)"

Please change this way in reprints. Probably apparent to most given the text following this: "...which may be either another object that implements required wrapping logic, or the original function itself". The "in" typo's clause was added in the 5th Ed.

Anonymous  Mar 23, 2014  May 02, 2014
PDF
Page 1274
3rd snippet

In illustrating decorators coded as factory functions that return a different callable object, the 2nd line of the comment reads

"# Return a different callable: nested def, class with __call__, etc"

It is the instance of a class with __call__ that triggers __call__ when used in a calling syntax. Calling a class would trigger __init__.
It might be more accurate to change that comment into

"# Return a different callable: nested def, class instance with __call__, etc"

The same comment line appears also on p.1278 and p.1282

Note from the Author or Editor:
REPRINTS: please change the three "class with __call__" to "class instance with __call__" in the "#" comments text on pages 1319, 1322, and 1327, adding "instance" per the post.

DISCUSSION: this is of course what was meant and implied here (an instance), and the comments were abbreviating for space. Given the potential ambiguity pointed out by the poster, though, it's worth the trivial patches.

Yu Cao  Mar 24, 2020  Jun 05, 2020
Printed
Page 1276
2nd example

I think a line is missing from the second example on page 1276:

def decorator(F): # F is func or method without instance
.... def wrapper(*args): # class instance in args[0] for method
.... # F(*args) runs func or method
.... return wrapper

Should it instead be the following?

def decorator(F): # F is func or method without instance
.... def wrapper(*args): # class instance in args[0] for method
.... F(*args) # F(*args) runs func or method
.... return wrapper

Note from the Author or Editor:
Minor and cosmetic, but this was broken by production. In my final draft, the "# F(*args)..." comment was indented 4 spaces under the nested def's header -- as it is in the very same skeleton code on the prior page. Production changed this to move just this comment to the right to align with others, despite the pattern in this section.

EDIT: to fix, move the comment in question to the left so its "#" is indented 4 spaces under the nested "def", like this:

def decorator(F): # F is func or method without instance
def wrapper(*args): # class instance in args[0] for method
# F(*args) runs func or method
return wrapper

In the end, most readers likely get the intent regardless of the comment's location, but this should follow the same structure as the other snippets. Note that using a real statement here (F(*args)) per the poster's query doesn't make sense, as this is not fully fleshed-out code; the pattern in this overview section is to use comments for hypothetical function bodies. Complete code examples appear later.

Jackson Coakley  Jul 20, 2015  Sep 04, 2015
Printed
Page 1284
1st code listing

This code listing shows an example of using attribute interception methods. But it appears that the comment on the following line was copied and pasted from the earlier section on properties:
sue = Person('Sue Jones') #sue inherits property too

Note from the Author or Editor:
Yes, and good catch. This was a result of copying the same test code from properties to attribute-interception methods -- deliberately, to demo the similarity, but this one comment doesn't apply. To fix, change the referenced comment text from the first of the following to the second, retaining the indentation and formatting (this is on page 124 in earlier printings):

# sue inherits property too
# sue's attrs work like bob's

Jason Gastelum  Sep 02, 2020  Dec 18, 2020
Printed, PDF, ePub, Mobi, , Other Digital Version
Page 1294
Very end of last paragraph on page

Trivial detail, but we need to add a missing colon (":") at the end of this paragraph, just before the last code listing at the end of the page.

The page number is from the PDF and print versions; in e-books, this is near the end of "Using descriptors to decorate methods" in Chapter 39.

Mark Lutz
 
Jun 30, 2013  Aug 16, 2013
Printed
Page 1297
1st full paragraph

The cited reason that __getattr__ is not called on print in 3.X when the Manager class does not define its own __repr__ method is that the inherited object.__repr__ method is looked up and run. But per page 855,the __str__ method is preferred for print (and str).

Note from the Author or Editor:
[Edit]
At the reference location on page 1297, please change:
Instead, a default __repr__ display method inherited
to:
Instead, a default __str__ display method inherited
This is in the last paragraph on page 1254 in older printings.

[Discussion only follows]
Agreed: because object defines both __str__ and __repr__, print() must be running the default __str__, not the default __repr__, since it tries __str__ first, then __repr__ (though some other contexts jump to __repr__ immediately).

The __str__ fallback in this example is also clearly noted just two pages earlier, and in the code's comments; see the first bullet on page 1295, and page 1293's comment (1252 and 1251 in earlier printings).

The edit might be mildly confusing given the use of __repr__ by all the examples here (and that may be why this typo snuck in), but Python's dual display methods are covered throughout the book, and at least one reader remembered the precedence rules covered 450 pages earlier.

Some background on this: display methods are a subtle special case; here's part of the story in 3.8 as it's presented to pure Python code:

# Instances get default displays for echoes and print()s
>>> class C: pass
>>> x = C()
>>> x
<__main__.C object at 0x7f92feb57d90>
>>> print(x)
<__main__.C object at 0x7f92feb57d90>

# Instances inherit both str and repr (and print() prefers the former)
>>> x.__str__
<method-wrapper '__str__' of C object at 0x7f92feb57d90>
>>> x.__repr__
<method-wrapper '__repr__' of C object at 0x7f92feb57d90>
>>> x.__str__ is x.__repr__
False

# But instances don't inherited other built-ins, like + and []
>>> x.__add__
AttributeError: 'C' object has no attribute '__add__'
>>> x.__getitem__
AttributeError: 'C' object has no attribute '__getitem__'

# The defaults are in object, at the top
>>> object.__str__ is object.__repr__
False
>>> object.__str__
<slot wrapper '__str__' of 'object' objects>

# They format the same in this case
>>> object.__str__(x)
'<__main__.C object at 0x7f92feb57d90>'
>>> object.__repr__(x)
'<__main__.C object at 0x7f92feb57d90>'

Strictly speaking, and as described by the book, there are really two reasons why print() runs object's __str__ in this example. First, the inherited object.__str__ disqualifies __getattr__, because __getattr__ runs only for attributes undefined by inheritance. This is what this post's edit tightens up.

Second, though, and more subtly, both __getattr__ and __getattribute__ are not invoked because built-in operations like print() skip the usual instance inheritance search path used for explicit attribute fetches, and instead jump straight to the class's preresolved implementation slots. This behavior is noted and demoed repeatedly in the text; to see it in full for yourself, you must study Python's C implementation code, including:

Python/bltinmodue.c, where builtin_print() calls PyFile_WriteObject()
Objects/fileobject.c, where PyFile_WriteObject() calls PyObject_Str()
Objects/object.c, where PyObject_Str() calls PyObject_Repr() iff no tp_str slot
Objects/object.c, where PyObject_Repr() shows a default display if no tp_repr

But this book intentionally limits its focus to the implications of all this for pure Python code. Full coverage of the nuances of Python's shifting C implementation are outside the scope of this book--and this reply.

Jason Gastelum  Sep 27, 2020  Dec 18, 2020
Printed, PDF, ePub, Mobi,
Page 1302
second code example (singletons.py), inline comment #5

The mention of "onCall" in that inline comment isn't formatted correctly ? while "onCal" is good (italicized and in "normal text" font), the last "l" isn't; it's printed in the monospaced code font again, also not italicized, just like actual code.

Note from the Author or Editor:
Yes -- minor but true. As suggested, make the last "l" character italics too, like the rest of the text in comment "# Rebinds Spam to onCall". It's one character in a 1600-page book, but it looks like a digit as is.

Julian R.  Mar 04, 2014  May 02, 2014
Printed, PDF, ePub
Page 1311
paragraph -2 (second last)

An expansion for clarity on decorator tradeoffs. On Page 1311, change the entire second last paragraph from:
"""
Decorators also promote code encapsulation to reduce redundancy and minimize future maintenance effort; although other code structuring tools do too, decorators add explicit structure that makes this natural for augmentation tasks.
"""
to this:
"""
Decorators also promote code encapsulation to reduce redundancy and minimize future maintenance effort; augmentation code appears just once in the decorator callable, instead of being copied for each deployment. Although manager functions can achieve this too, decorators also offer an explicit syntax and seamless call model that makes them natural for augmentation tasks.
"""
This should add 2 lines, there seems to be room on the page, but please contact me if this changes page breaks.

Mark Lutz
 
Oct 18, 2014  Oct 29, 2014
, Printed, PDF, ePub, Mobi, , Other Digital Version
Page 1327
2nd last line of 2nd paragraph on page

Minor typo that eluded all proofing: "that" should be "the".
Change: "we?ll also employ that third of the mix-in options"
to: "we?ll also employ the third of the mix-in options".

Mark Lutz
 
Jul 11, 2013  Aug 16, 2013
Printed
Page 1332
1st full code listing

Comment on call to spam(1, 2, 3) reads "Really calls wrapper, bound to func," but should read "...bound to spam."

Note from the Author or Editor:
Yes, though this is a bit gray, and depends on how "bound" is being used here. Assuming it means assignment, does it refer to that of the "func" state-retention variable, or the decorated "spam?" For instance, if "bound to func" means "func has been assigned to spam," then the clause seems to work as is, but it's too terse to call (and impossible to reconstruct at this point).

That said, this chapter often uses "bound" (and sometimes "rebound") to refer to the automatic and implicit assignment to the decorated name, and the parallel comment about eggs just a few lines ahead uses "bound to eggs" in this sense. The two cryptic comments should at least agree in meaning.

So, for symmetry at the least, let's change the referenced comment from the first of the following to the second (keeping all formatting/spacing, and this is on page 1287 in earlier printings):

# Really calls wrapper, bound to func
# Really calls wrapper, bound to spam

All of which seems like an awful lot of bother over one small word in a comment, but such is the nature of tech docs.

Jason Gastelum  Nov 02, 2020  Dec 18, 2020
Printed
Page 1334
3rd paragraph

In the sentence "we also have to be careful about the distinction
Python makes between decorators coded as callable class instance objects and decorators coded as functions."

Here, the "decorators" should be coded as a class, the decorated function is indeed coded as a callable decorator class instance object.

Note from the Author or Editor:
This isn't required, but to avoid the post's confusion, let's change the referenced:
decorators coded as callable class instance objects and decorators
coded as functions
to this:
decorators based on callable class instance objects and decorators
based on nested functions
which changes both "coded as" to "based on", and adds a "nested".

[Discussion] I believe I understand the post's point, but it seems to be splitting hairs. Everything in the example is "coded" in some sense, whether this refers to the actual "class" statement or to the "@tracer" syntax where the class's instance is generated. Either way, the instance must factor into the description: the class's __call__ is invoked through the instance, and what we're really comparing here is __call__ invocations to simple function invocations (not the initial "@" invocation of the class's __init__).

To avoid future confusion, though, dropping the "coding" terminology may make the instance/function distinction marginally more symmetric to some readers, and doesn't lose anything in the shuffle. This section also covers decorators based on classes with a __get__ descriptor, but that's far too much for a humble segue to handle... [This is on page 1289 in earlier printings.]

Wenjie Wang  Oct 29, 2018  Mar 22, 2019
Page 1342
last line of user input

Quotation marks missing.

timeit.timeit(number=1, stmt=lambda: listcomp(1000000))

should be

timeit.timeit(number=1, stmt='lambda: listcomp(1000000)')

or

timeit.timeit(number=1, stmt="lambda: listcomp(1000000)")

Note from the Author or Editor:
No. As stated by the book on page 665 (in Chapter 21's coverage referenced by this example), the Python timeit module accepts either a string object or a no-argument callable object for its "stmt" argument. In the example, the lambda simply defers execution the same as using a string. See Chapter21 and the Python manual for more info:

https://docs.python.org/3/library/timeit.html#timeit.Timer

No changes required, retained for other readers.

Philip Muench  Mar 14, 2023 
Printed
Page 1354
2nd code listing

In the comments of the second code listing the Tracer decorator is referred to as Wrapper three times. And to be really picky, in all three places it should read ''instance of Tracer' rather than 'Wrapper.'

Note from the Author or Editor:
Yes--you're right that all the "Wrapper" here should be "Tracer" (this looks like another copy/paste of identical test code, though it was tweaked a bit). But adding "instance" in the comments seems a bit excessive; that should be implicit and understood by page 1354, and "a Tracer" seems enough. And the first comment seems to have botched its meaning altogether.

So, let's change the following 3 referenced comments from this:

# Wrapper bound to Person
# bob is really a Wrapper
# Wrapper embeds a Person

To this, as usual retaining all the originals' indentation and formatting:

# Person rebound to a Tracer
# bob is really a Tracer
# Tracer embeds a Person

This example is on page 1308 for readers of older reprints. Also changed type to "typo;" this is in comment text, not executable code.

Jason Gastelum  Dec 06, 2020 
Printed, PDF, ePub, Mobi,
Page 1363
3rd paragraph

The text reads:

"decorators must provide class behavior is less direct ways."

should read:

"decorators must provide class behavior in less direct ways."

Note from the Author or Editor:
Yes -- please correct as suggested (and consider hiring this reader for copyedits!).

Anonymous  Mar 26, 2014  May 02, 2014
Printed
Page 1366
Final paragraph

"Because the notion of type is the same as class today, we can subclass type with normal object-oriented techniques and class syntax to customize it."

Perhaps change this to:

"...we can subclass type with normal object-oriented techniques and use class syntax to customize it."

Note from the Author or Editor:
Agreed that this sentence may be a bit awkward, but let's change it the following way to better retain its original meaning (just moving the 2nd sentence's final clause ahead, and retaining all its present word formatting):

"Because the notion of type is the same as class today, we can subclass type to customize it with normal object-oriented techniques and class syntax."

Jackson Coakley  Jul 20, 2015  Sep 04, 2015
Printed, PDF, ePub
Page 1378, 1383, 1490, 1487
see individual edits for page locations

Four minor edits for clarity or readability, inspired by a recent reread of Appendix B:

1) Page 1378, paragraph 3, --change--:
When __debug__ is False, the decorator returns the origin function
--to--:
When __debug__ is False, the decorator returns the original function

2) Page 1383, top of page, --change--:
names, in order to determine which position arguments actually appear in
--to--:
names, in order to determine in which position arguments actually appear

3) Page 1490, paragraph 4, --change--:
This works both when launching scripts and starting the interactive interpreter
--to--:
This works when both launching scripts, and starting the interactive interpreter

4) Page 1487, paragraph 1, --change--:
Though the new launcher comes with
--to--:
This new launcher provides an extra layer of code that chooses and starts an installed Python. Though it comes with

Mark Lutz
 
Oct 02, 2018  Oct 12, 2018
Printed
Page 1378
1st code listing

The following comment: "Or person if -O cmd line argument" should read "Or persinfo if -O cmd line argument". I think you were trying to say that the original, undecorated function is invoked with the -O option as you do a few lines below.

Note from the Author or Editor:
Yes -- please change the "person" to "persinfo" in the referenced comment as suggested by the post. This does mean the original, undecorated version.

Jason Gastelum  Jan 02, 2021  Jul 02, 2021
Printed, PDF, ePub
Page 1384
Step 1 of the matching algorithm at page bottom

Picking nits, perhaps, but it looks like the font of "N" in the first algorithm step "1. Let N be the number..." differs from that of "N" everywhere else in this list. Change the font of this first "N" to match the others. (This is on page 1338 in older printings.)

Mark Lutz
 
Mar 31, 2018  May 04, 2018
Printed, PDF, ePub, Mobi, , Other Digital Version
Page 1386
Just before

Reprints: please contact me when applying patches in the next reprint--it looks like there is a bit of room on this page and later, and I may opt to add a few more details here drawn from a recent article on the subject (which is also to be posted on O'Reilly's site as I write these words):

https://learning-python.com/python-newstyle-inheritance.html

Readers: you'll find another look at inheritance in this article, though its extra details are all implied by material and examples surrounding the algorithm in the book's more in-depth coverage.

Mark Lutz
 
Jun 30, 2013  Aug 16, 2013
Page 1426
1st code listing

The comment on the Sub class incorrectly states "Classes inherit from superclasses but not from metaclasses." In fact, classes do inherit from metaclasses; it's instances that don't.

Note from the Author or Editor:
This comment merits an edit, but it's not incorrect for the section in which it appears. In short, "inheritance" has a specific usage near this comment, where it applies to instance/class search only, not class/metaclass. Because this term is also used more generally later, though, it's worth an edit to minimize confusion.

[EDIT] Please extend the comment on this page from the first of the following to the second:

# But not from metaclasses
# But not from metaclasses for instance access

Also, an unrelated formatting issue: a bit further down on this page, the "r" at the end of the following comment is formatted oddly -- please fix:

# Inherited from Super

[DISCUSSION] As described in depth by the narrative and code around this comment, it's true that classes obtain attributes from metaclasses when accessed directly, but not when the reference originates at an instance. In fact, this is one of the primary points of this section.

More profoundly and subtly, though, the section of the book containing this comment deliberately uses terminology to draw a pseudo-mathematical distinction between the two (nearly) orthogonal attribute-tree searches in new-style classes:

- "Inheritance" refers to the normal instance/class relationship
- "Instance" refers to the special class/metaclass relationship

This usage of these two terms is noted explicitly earlier in this chapter, in the paragraph that begins "Notice that" on page 1413. The former applies to attribute searches started at an instance, and the latter is an extra step for attribute searches begun at a class only. (Technically, class-level accesses first search the superclass tree also searched last for instances, which is why the two lookups are not quite orthogonal.) In addition, this section uses the term "acquired" to distinguish names obtained from the "instance" tree.

In this intended sense, the book's adjacent comments are accurate, because "inheritance" has been reserved for the instance/class tree lookup only:

# Classes inherit from superclasses
# But not from metaclasses

Admittedly, though, this grows potentially confusing when later sections of this chapter generalize the term "inheritance" to refer to attribute lookup in both class and metaclass trees (and show that the only real difference is the extra step into the metaclass tree for class-based fetches). Hence, augmenting the comment as described reduces possible later confusion, even though the comment is not incorrect for the context in which it appears.

Jason Gastelum  Mar 10, 2021  Jul 02, 2021
Printed, PDF, ePub, Mobi,
Page 1447 (and TOC)
First bullet item, 2nd header line, TOC entry of latter

On this page, change "!#" to "#!" in the two appearances of the text "Unrecognized Unix !# lines" noted in Location. The second is a header line, so this must also be fixed in its Table of Cotents entry. Minor and likely obvious, if noticed at all; the correct "#!" shows up 107 times in the book, and these 2 look like a single finger slip that was copied, but escaped multiple proofs.

Mark Lutz
 
Jan 12, 2014  Jan 24, 2014
, Printed, PDF, ePub, Mobi, , Other Digital Version
Page 1457
line 2 of paragraph 4

Trivial typo, singular->plural: change "code written for prior release in the 3.X line" to "code written for prior releases in the 3.X line". (Typos happen In a 1600-page book, despite the best of proofing efforts.)

Mark Lutz
 
Jul 15, 2013  Aug 16, 2013
PDF
Page 1473
3rd paragraph

This is very minor issue:

"This usually only requires you to unpack the file and run simple config
and make commands"

The proper name for first command is configure.

Note from the Author or Editor:
[On page 1425 para 2 in printings prior to Sep-2016]
Yes - change "config" to "configure" as suggested. (Of course; no idea how the abbreviation took hold here.)

Grzegorz Szpetkowski  Sep 15, 2016  Jan 06, 2017
PDF
Page 1478
2nd paragraph in sidebar

The command "yum tkinter" is incorrect. The correct one, in order to install tkinter package is "yum install tkinter".

Note from the Author or Editor:
[On page 1429 para 5 in printings prior to Sep-2016]
Yes - change this command as suggested, adding the "install" sub-command word in literal font. (If we had space, I'd also add the Ubuntu-friendly "sudo apt-get install python3-tk", but this sidebar may already be more detail than should have been attempted.)

Grzegorz Szpetkowski  Sep 15, 2016  Jan 06, 2017
PDF
Page 1482
Middle of the page

The comment in the middle of the page reads:

# Interactve

It should read:

# Interactive

Note from the Author or Editor:
Yes: a minor typo but please correct as described, changing the comment's text "Interactve" => "Interactive". This is on page 1433 in printing prior to Sep-2016.

XEstrada  May 29, 2017  Jul 14, 2017
Printed
Page 1492
third line

"3,1" should change to "3.1" in the sentence:
On my machine I currently have Pythons 2.7, 3,1, 3.2, and 3.3 all installed;

Note from the Author or Editor:
Yep; please correct this as described ("3,1" => "3.1"). Typos happen - and somehow manage to elude capture for years.

Anonymous  May 05, 2020  Jun 05, 2020
Printed, PDF, ePub, Mobi, , Other Digital Version
Page 1505
# Fetch and open/play a file by FTP

userinofo = ()
connection.login(*userinfo)

Note from the Author or Editor:
Yes--a typo (and a good catch: this is in self-study code, in a branch run only for anonymous FTP which was never exercised in testing). To patch, please change code line -3 on this page (per PDF paging) from:
userinofo = ()
to:
userinfo = ()

Anonymous  Aug 20, 2013  Nov 08, 2013
ePub, Mobi,
Page 1541
About the Author bio at end of some versions (only)

The author bio at the end of the EPUB and MOBI e-books and the Safari online releases is an abbreviated version that does not match the full and final bio that appears in both the print and PDF e-book versions. Not critical, but for consistency, please use the print/PDF's full and final author bio in all releases.

Mark Lutz
 
Jun 30, 2013  Aug 16, 2013
Printed, PDF, ePub, Mobi,
Page 9999
URL above the last paragraph in the page

The following is the Safari Online URL for the page in error:

http://proquest.safaribooksonline.com/book/programming/python/9781449355722/preface/pr04s09_html?uicode=califa

(If there is a better way to locate the problem location in the online text, please let me know.)


The problem is with the HREF associated with the following link:

http://rmi.net/~lutz

... it justs hangs there. However, modifying the HREF to

http://www.rmi.net/~lutz

works fine.

N.B.

The page number I entered for this issue - 9999 - is entered only to satisfy the requirement of that being a required field.

Note from the Author or Editor:
This is on the middle of Page xlviii of the Preface. Replace:
http://rmi.net/~lutz
to:
http://www.rmi.net/~lutz
in both the text and the underlying ebooks' link.

This is the only spot in the book where the short and now invalid form of this URL appears. It reflects a change at my ISP -- Earthlink, which owns rmi.net after multiple shuffles, dropped the non-"www" domain name after the book was published. The longer "www" form of this domain does appear just above on the same page, and a web search should find the proper form too (and may be required if www.rmi.net ever goes away altogether), but it's worth a patch.

pob  Mar 23, 2014  May 02, 2014