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.
| Version |
Location |
Description |
Submitted by |
Date submitted |
| PDF |
Page P127&128
P127 2nd paragraph to last, P128 1st paragraph |
Since you changed the 5th paragraph on P127, you should change these two places too, namely im_self, im_func, and im_class.
|
Anonymous |
Mar 26, 2025 |
| PDF |
Page 26
Below and to the right of the picture of lemur |
This is not an error, but a suggestion for improvement. In this piece of code …
' >>> print(list(enumerate("abc"))) '
… 'print' and the corresponding parentheses are redundant, because we are in the interpreter mode here.
|
Vadim Flyagin |
Nov 11, 2025 |
| PDF |
Page 41
Table 3-1, 1st rows of descriptions of methods from_bytes and to_bytes |
Since Python 3.11, which is covered in the book, default argument value for byteorder was added in method int.from_bytes, and also default argument values for length and byteorder were added in method int.to_bytes, so 1st rows of descriptions of these methods should be changed so:
int.from_bytes(bytes_value, byteorder='big', …
i.to_bytes(length=1, byteorder='big', …
|
Vadim Flyagin |
May 06, 2025 |
| PDF |
Page 55
paragraph adjacent to “Augmented assignment” section from above |
“Each of these forms requires x to have at least two items.” — this is true with respect to 1st form, but wrong with respect to the 2nd one: 2nd form “requires x to have at least” ONE item. If, for instance, “x” equals “[1]”, then “x[1:-1]” will successfully result in an empty list and it will be successfully assigned to “middle”.
|
Vadim Flyagin |
Aug 24, 2025 |
| Printed |
Page 65
1st paragraph |
Original text that contains error:
With a negative stride, in order to have a nonempty slice, the second ("stop") index needs to be SMALLER than the first ("start") one.
Error: The word "SMALLER" is incorrect. For example:
a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
The result of a[-1:0:-1] is [9, 8, 7, 6, 5, 4, 3, 2, 1], where the second index (0) is GREATER than the first index (-1).
|
Daffle F. Lauren |
May 17, 2025 |
| Printed |
Page 66
the 2nd code examples on this page |
Original code example that contains error:
x[1:4] = [88, 99] # x is now [10, 88, 99, 40, 50]
The correct version of the above code example shall be:
x[1:4] = [88, 99] # x is now [10, 88, 99, 50]
|
Daffle F. Lauren |
May 17, 2025 |
| PDF |
Page 125
Getting an attribute from an instance |
#### **Error description:**
The book's description of the attribute lookup process states that when accessing an attribute `x.name`, Python first checks if `name` exists as an overriding descriptor in the class `C` *or any of its ancestor classes*. This implies that the entire Method Resolution Order (MRO) is scanned for a data descriptor *before* any other lookup steps are taken.
However, this is incorrect. The actual behavior, as confirmed by the official Python documentation and empirical testing, is that Python finds the **first** occurrence of the attribute in the MRO and *then* determines how to proceed based on whether that first match is a data descriptor, a non-data descriptor, or a regular attribute.
As a result, a data descriptor in an ancestor class will **not** override a non-data-descriptor or even a regular attribute of the same name in a descendant class.
-----
#### **The problematic text:**
> When you use the syntax `x.name` to refer to an attribute of instance `x` of class `C`, lookup proceeds in three steps:
>
> 1. When `name` is in `C` (or in one of `C`'s ancestor classes) as the name of an overriding descriptor `v` (i.e., `type(v)` supplies methods `__get__` and `__set__`) the value of `x.name` is the result of `type(v).__get__(v, x, C)`.
> 2. Otherwise, when `name` is a key in `x.__dict__`, `x.name` fetches and returns the value at `x.__dict__['name']`.
> 3. Otherwise, `x.name` delegates the lookup to `x`'s class...
The key issue is in step 1's phrase "or in one of C's ancestor classes", which suggests a full preliminary scan.
-----
#### **Example that shows the discrepancy:**
This code shows that a regular class attribute (a string) in an intermediate class is found and used before the overriding (data) descriptor of the same name in an ancestor class.
```python
class OverridingMethodDescriptor:
"""A simple data descriptor with both __get__ and __set__."""
def __get__(self, instance, owner):
return "I am from the Grandparent's data descriptor"
def __set__(self, instance, value):
raise AttributeError("Cannot set")
class Grandparent:
# The data descriptor is defined in the highest ancestor.
special_attribute = OverridingMethodDescriptor()
class Parent(Grandparent):
# A regular, non-descriptor attribute is defined in the intermediate class.
special_attribute = "I am a simple string in Parent"
class Child(Parent):
# This class is empty and inherits from Parent.
pass
# MRO for Child is: (Child, Parent, Grandparent, object)
# The lookup for 'special_attribute' will find the string in Parent first.
c = Child()
print(c.special_attribute)
# Expected output based on the book's description (data descriptor should win):
# "I am from the Grandparent's data descriptor"
# Actual output (the first match in the MRO wins):
# "I am a simple string in Parent"
```
-----
#### **Suggested correction:**
The description of the lookup process should be rephrased to reflect that the MRO is traversed only once to find the first match. The logic then proceeds based on the type of that match.
A more accurate description of the lookup for `x.name` would be:
1. Search the MRO of `type(x)` for the **first occurrence** of the name `name`. Let this be `attr`. If not found, raise `AttributeError`.
2. If `attr` is a data descriptor (i.e., it has a `__set__` or `__delete__` method), return the result of its `__get__` method. The lookup stops here.
3. Otherwise, check if `name` exists in the instance's `__dict__`. If it does, return that value. The lookup stops here.
4. Otherwise, if `attr` is a non-data descriptor (i.e., it has a `__get__` method but no `__set__`), return the result of its `__get__` method.
5. Otherwise (if `attr` is a regular class attribute), return `attr` itself.
This revised logic correctly explains the behavior seen in the demonstration code and aligns with the official Python documentation on descriptors (in particular the definition of `object_getattribute`).
|
Hyun-U Sohn |
Sep 01, 2025 |