Errata

Fluent Python

Errata for Fluent 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.

The following errata were submitted by our customers and have not yet been approved or disproved by the author or editor. They solely represent the opinion of the customer.

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

Version Location Description Submitted by Date submitted
PDF Page downloaded code, 02-array-seq/
na, in code downloaded

running test.sh
shows
1 items had failures:
2 of 3 in bisect_demo
***Test Failed*** 2 failures.
Traceback (most recent call last):
File "/usr/lib64/python3.10/runpy.py", line 196, in _run_module_as_main
return _run_code(code, main_globals, None,
File "/usr/lib64/python3.10/runpy.py", line 86, in _run_code
exec(code, run_globals)
File "/usr/lib64/python3.10/doctest.py", line 2806, in <module>
sys.exit(_test())
File "/usr/lib64/python3.10/doctest.py", line 2794, in _test
m = __import__(filename[:-3])
ModuleNotFoundError: No module named 'metro_lat_long'
ERROR: usage: pytest [options] [file_or_dir] [file_or_dir] [...]
pytest: error: unrecognized arguments: --nbval
inifile: /data/files/python/fluent/example-code-2e/02-array-seq/pytest.ini
rootdir: /data/files/python/fluent/example-code-2e/02-array-seq

Dave Pawson  Apr 06, 2022 
No page numbers on O'Reilly Learning platform!
4th paragraph before 'Further Reading' section of Chapter 11

This paragraph includes the sentence:
"We then implemented __hash__ using the recommended technique of xor-ing the hashes of the instance attributes."
However this is not how __hash__ is actually implemented in the chapter:
"The __hash__ special method documentation suggests computing the hash of a tuple with the components, so that’s what we do in Example 11-8."

Oktay Safak  Apr 11, 2022 
No page numbers on O'Reilly Learning platform.
3rd paragraph before 'Further Reading' section of Chapter 11

This paragraph contains the sentence: "Because using __slots__ has side effects, it really makes sense only when handling a very large number of instances—think millions of instances, not just thousands. In many such cases, using *pandas* may be the best option."

However, what is actually recommended in the TIP box just before the "Summarizing the Issues with __slots__" section is the numpy library, not pandas:

'If you are handling millions of objects with numeric data, you should really be using NumPy arrays (see “NumPy”)'


OKTAY SAFAK  Apr 11, 2022 
Other Digital Version Chapter 4 in the Warning box just before Example 4.3
WARNING box

This text in this warning is based on an early draft of PEP 467 from 2014. Search for 3.6 on this page:

https://mail.python.org/pipermail/python-ideas/2014-March/027376.html

As of August 2022 the status of PEP 467 is still 'draft' and in practice the deprecation and removal have never been implemented.

In Python 3.10 it is still possible to call bytes or bytearray with a single integer to create a binary sequence of that size initialized with null bytes:

>>> bytes(4)
b'\x00\x00\x00\x00'

>>> bytearray(5)
bytearray(b'\x00\x00\x00\x00\x00')

Jack Andeson  Aug 03, 2022 
Printed, PDF Page xxii
first paragraph

async dev should be async def

Anonymous  Sep 11, 2022 
Printed Page Page 891 - LineIten Take #4
Last paragraph before the example

I think the following paragraph:

"In Example 23-3, the LineItem descriptor class doesn't need an __init__. Instead, __set_item__ saves the name of the storage attribute."

Should read (two changes):

"In Example 23-3, the Quantity descriptor class doesn't need an __init__. Instead, __set_name__ saves the name of the storage attribute."


Just want to say thank you for this excellent book. I'm learning Python finally ;)

Rik Huygen  Dec 29, 2022 
Printed Page Page 23, figure 2-1.
Top of figure

The list in both the tuple and the array contains a period where there should be a comma. Also the formatting of the list in the array is missing a space:

(9.46, 'cat', [2.08. 4.29])

array('d', [9.46,2.08. 4.29])

Peter van der Heijden  Jan 05, 2023 
Printed Page Page 356
Bottom of page circled 2

"Promotion" refers to the "promotion" decorator and not to the "Promotion" type alias and should therefore not be capitalized.

Peter van der Heijden  Jan 14, 2023 
Explanation for example 19-14
Callout #8

The sentence says:
Get one PrimeResult. Calling .get() on a queue block until there is an item in the queue.
But should say:
Get one PrimeResult. Calling .get() on a queue blocks until there is an item in the queue.

David Whittaker  May 04, 2023 
Printed Page Page 629
Table 17-4

The keyword argument for itertools.permutations seems to be wrong. In the text, it is presented as permutations(it, out_len=None), where out_len is the key word argument. However, that results in a TypeError for Python 3.10 -

list(itertools.permutations('ABC', out_len=2))
Traceback (most recent call last):
File [FILE_PATH], line 3433, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-5-2aad04d92073>", line 1, in <module>
list(itertools.permutations('ABC', out_len=2))
TypeError: 'out_len' is an invalid keyword argument for permutations()

This key word argument also does not appear in the itertools documentation. Rather, the correct keyword appears to be 'r':

list(itertools.permutations('ABC', r=2))
Out[9]: [('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')]

Alexander Lapanowski  May 12, 2023 
PDF Page Page 482 (pdf)
Example 8-18. mode_hashable.py

In Example 8-18...

# tag::MODE_HASHABLE_T[]
from collections import Counter
from collections.abc import Iterable, Hashable
from typing import TypeVar

HashableT = TypeVar('HashableT', bound=Hashable)

def mode(data: Iterable[HashableT]) -> HashableT:
pairs = Counter(data).most_common(1)
if len(pairs) == 0:
raise ValueError('no mode for empty data')
return pairs[0][0]
# end::MODE_HASHABLE_T[]


def demo() -> None:
import typing

pop = 'abracadabra'
m = mode(pop)
if typing.TYPE_CHECKING:
reveal_type(pop)
reveal_type(m)
print(pop)
print(m.upper(), type(m))


if __name__ == '__main__':
demo()

My understanding from both Fluent Python and the Typing documentation is that the annotations should allow for return of an object with a Type that is consistent with Hashable.

To test this, I created a class which subclasses Hashable and tried returning an object of this Type:

from collections import Counter
from collections.abc import Hashable, Iterable
from typing import TypeVar

HashableT = TypeVar("HashableT", bound=Hashable)


class ModeResult(Hashable):
def __init__(self, value: HashableT, count: int):
self.value = value
self.count = count

def __hash__(self):
return hash(self.value)


def mode(data: Iterable[HashableT]) -> HashableT:
pairs = Counter(data).most_common(1)
if len(pairs) == 0:
raise ValueError("no mode for empty data")
value, count = pairs[0]
res = ModeResult(value, count)
return res


def demo() -> None:
import typing

pop = "abracadabra"
m = mode(pop)
if typing.TYPE_CHECKING:
reveal_type(pop)
reveal_type(m)
print(pop)
print(m, type(m))


if __name__ == "__main__":
demo()

But when I run this, mypy complains:

error: Incompatible return value type (got "ModeResult", expected "HashableT") [return-value]

Am I misunderstanding something?
Can you show an example where the function returns an object of a Type that is consistent with Hashable (but is distinct from the data variable passed to the function?)

Craig Richards  Nov 30, 2023 
Printed Page 10
code: v1 = Vector(2, 4) ...

The codes v1 = Vector(2, 4) and the rest on page 10 show an error:

name 'Vector' is not defined

I couldn't find any class definition of Vector.


Anonymous  Jan 16, 2023 
Other Digital Version 55
Example 2.17

Section "A += Assignment Puzzler": The list of possible answers to the puzzle is numbered numerically (1 to 4), but answer 4 refers to "Both A and B", which either should read "Both 1 and 2" or the list should be numbered with letters.

David Zerrenner  May 05, 2022 
Printed Page 68
Table 2-4

The deque implements some of the missing methods starting from python 3.5, see the official documentation:

docs.python.org/3/library/collections.html#collections.deque

These include:
s.__add__
s.__contains__
s.copy()
s.index(e)
s.insert(p, e)
s.__mul__
s.__imul__
s.__rmul__

Radek Ježek  Dec 27, 2022 
Printed Page 89
footnote

"method as a first-class function" -> "method as a first-class object"

Anonymous  Nov 26, 2022 
PDF Page 166
Code block

In the code for Example 5-2, book's output gives the answer False, for
`
>>> issubclass(Coordinate, typing.NamedTuple)
False
`
But in Python version 3.10.8 it is actually a TypeError with the message, issubclass() arg 2 must be a class, a tuple of classes, or a union

Fazil Amirli  Dec 19, 2022 
PDF Page 247
2nd paragraph

Minor error, last sentence of the second paragraph, text is "That is our next subject. *Bold Text*opmod07".

Anonymous  Jul 04, 2022 
Printed Page 268
First paragraph

In the second sentence, the entirety of the first instance of the phrase “is subtype-of” is italicized, while the second instance only has “subtype-of” italicized. The phrase is partially italicized when introduced on page 267.

Samuel Jenkins  Jan 02, 2023 
PDF Page 276
Example 8-13

num_rows, reminder = divmod(len(sequence), num_columns)
num_rows += bool(reminder)

should read:

num_rows, remainder = divmod(len(sequence), num_columns)
num_rows += bool(remainder)

Anonymous  Jan 19, 2023 
PDF Page 292
4th line

The following example in Chapter 8 ("Types Usable in Annotations" > "Callable") contains a syntax error (extra "]"):

def repl(input_fn: Callable[[Any], str] = input]) -> None:

I think this is the correct function signature:

def repl(input_fn: Callable[[Any], str] = input) -> None:

E M  Jul 10, 2022 
Printed Page 316
Example 9-11

The output from "avg.__closure__[0].cell_contents" should read "[10, 11, 15]" instead, given we called the "avg" function three times with 10, 11, and 15 respectively in Example 9-9.

Jerry Wang  Jan 08, 2024 
Printed Page 394
3rd paragraph

The paragraph speaks of "xor-ing the hashes of the instances attributes", but on page 378, there is no XOR operation to be found. The next chapter, 12, introduces the class Vector, which DOES use XOR on all the hashes of the attributes. This is better categorized as a medium serious technical issue, as it left me going back and forth, between the two pages looking for a nonresistant XOR.

Daniel S. Cox  Jan 25, 2024 
Printed Page 398
2nd paragraph

There is a line ending with the following test, it appears the underscores are accidental and perhaps were intended to put Fluent Python in italics:

in the _Fluent Python_ code repository).

Jonathan Grindstaff  Apr 10, 2023 
PDF Page 412
TIP

in the first sentence.

`In “Dual-Mode str and bytes APIs” we saw functions that accept either str or bytes arguments but return str if the argument was str or bytes if the arguments was bytes. `

i thought it should be `In “Dual-Mode str and bytes APIs” we saw functions that accept either str or bytes arguments but return str. `

harumonia  Aug 09, 2023 
PDF Page 414
warning section

but for *, & it should be 1

for the bitwise and operation, the identity value should be 0b11111111... instead of 0b1

Yifan Pan  Sep 21, 2022 
PDF Page 440
Defensive Programming and “Fail Fast” paragraph

"[…] for example, rejecting invalid arguments right a the beginning of a function body." → "[…] for example, rejecting invalid arguments right aT the beginning of a function body."

Krzysztof Mierzejewski  Feb 28, 2023 
PDF Page 461
Figure 13-7

"list" type does not have "__bool__" method implemented.

Anonymous  Jan 20, 2024 
Printed Page 472
3rd paragraph

..., but it turns out this is example not a particularly good ...

Andreas Kanz  Aug 29, 2022 
Printed Page 474
warnings block

isssue written with a 3rd "s"

Andreas Kanz  Aug 29, 2022 
Printed Page 503
3rd paragraph: "Since every method ot UpperCaseMixin..."

Since every method ot UpperCaseMixin calls...

should be

Since every method of UpperCaseMixin calls...

Anonymous  Jul 06, 2022 
Printed Page 511
General Note

The General Note has "...it is interesting to note that the some of the deepest..." when it should be "...it is interesting to note that some of the deepest..." as the first article is unnecessary.

Samuel Woodward  Feb 16, 2023 
Printed, PDF Page 539
the last sentence in a quote from PEP 563 at the top of the page

The word "annotations" is written in italics and has no surrounding underscores.

Alexander Tarasov  Jan 23, 2024 
Printed, PDF Page 540
2nd item in a bulleted list at the bottom of the page

The word "in spect" is written with an extra space, must be: "inspect".

Alexander Tarasov  Jan 23, 2024 
Printed Page 580
Table 16-1

Table 16-1 refers to an infix operation "__idivmod__" which does not exist. Unlike the other operators in the table, divmod does not support augmented assignment; see the section "emulating numeric types" of the python data model reference for confirmation of this.

Alden Bradford  Dec 16, 2022 
Printed, PDF Page 617
2nd paragraph

The ArithmeticProgression class from ... and is a another example → is another example

Alexander Tarasov  Feb 06, 2024 
Printed, PDF Page 629
5th item in the code explanation section

"Here the reverse generator...": reverse -> reversed

Alexander Tarasov  Feb 24, 2024 
Printed, PDF Page 690
5th paragraph

...a short post explaining why its useful -> it's useful

Alexander Tarasov  Mar 25, 2024 
PDF Page 698
1st & 2nd paragraphs

```
Process:
... Processes allow preemptive multitasking: the OS scheduler preempts—i.e., suspends — each running process periodically to allow other processes to run. ...

Thread:
... Like processes, threads also enable preemptive multitasking under the supervision of the OS scheduler. ...
```

At least, this is only true for some operating systems.
Technically, in the Windows OS, the scheduler works with threads (execution units) but not processes.
There is quite a common situation when the scheduler preempts one thread of the process to allocate a time quantum for another thread of the same process (i.e., no concept of suspending a process).

Vitaliy Yelnik  Aug 21, 2023 
Other Digital Version 771
Bullet point number 3

Descriptive text for bullet 3 uses "HTTPStetusError" instead of "HTTPStatusError"

Rolf  Aug 28, 2022 
PDF Page 786
3rd paragraph

```
Now let’s study the async with statement that appeared in supervisor
(Example 21-2) and get_flag (Example 21-3).
```

But get_flag doesn't contain `async with` statement:
```
async def get_flag(client: AsyncClient, cc: str) -> bytes:
url = f'{BASE_URL}/{cc}/{cc}.gif'.lower()
resp = await client.get(url, timeout=6.1,
follow_redirects=True)
return resp.read()
```

Vitaliy Yelnik  Aug 28, 2023 
Printed Page 796
Code block, inside "supervisor" function

The point where the error is initialised (indicated by point 6) is placed just outside the following for loop:

error: httpx.HTTPError | None = None
for coro in to_do_iter:
...

I believe the initialisation of the "error" variable should happen inside the for loop, so for every coro check it is initially None instead of carrying over the value from previous loop.

Thanks for an amazing book! Really enjoying it, and learning heaps!

Barend Linders  Jul 10, 2022 
PDF Page 820
First tip/suggestion box (green squirrel)

If I'm reading it correctly, the text implies that the only difference between using `await` in a list comprehension (e.g. `[await fun() for fun in funcs]`) and using `asyncio.gather` is error handling.

This is misleading, because in the listcomp, coroutines will be executed serially, so it is much slower and not async. Minimal reproducing example:

```
async def async_main():
t0 = time.perf_counter()
await gather()
print(f"Gather took {time.perf_counter() - t0:.2f}s")

t0 = time.perf_counter()
await listcomp()
print(f"listcomp took {time.perf_counter() - t0:.2f}s")

async def gather():
tasks = [do_work(name) for name in names]
await asyncio.gather(*tasks)

async def listcomp():
[await do_work(name) for name in "a b c".split()]

async def do_work(name: str):
await asyncio.sleep(3)
return name

if __name__ == '__main__':
asyncio.run(async_main())
```

Output:
Gather took 3.00s
listcomp took 9.00s

Rusheb Shah  Aug 03, 2023 
PDF Page 855
next to last paragraph

Chapter 23 will include a through explanation about overriding versus nonoverriding descriptors. -> a thorough explanation

Anonymous  Jan 05, 2023 
PDF Page 867
Example 22-29

Note #2 of the Example 22-29 says "Using vars to inspect the nutmeg instance". The code example itself doesn't use vars, it reads from the __dict__ property.

Roman  Sep 04, 2023 
PDF Page 890
Example 23-5

Note #3 of the Example 23-5 says "validate is an abstract method; this is the template method.", whereas earlier it was said "In Example 23-5, Validated.__set__ is the template method and self.validate is the abstract operation.".

Indeed, __set__ method is a template method, not validate.

Roman  Sep 05, 2023 
PDF Page 932
Figure 24-2

str, object, and LineItem are instances type -> instances of type

Anonymous  Jan 18, 2023 
PDF Page 948
last paragraph

If that Persistent metaclass is -> PersistentMeta metaclass

Anonymous  Jan 18, 2023 
PDF Page 949
Footnote 16

stay way from -> away

Anonymous  Jan 18, 2023 
PDF Page 954
2nd and 3rd paragraphs under Further Reading

There are two occurrences of "the The Python Standard Library". Did you actually mean it that way?

Anonymous  Jan 18, 2023 
PDF Page 960
Further Reading

what it its to be “Pythonic” -> what it is

Anonymous  Jan 18, 2023