Learning Python by Mark Lutz & David Ascher Unconfirmed error reports are from readers. They have not yet been approved or disproved by the author or editor and represent solely the opinion of the reader. Here's a key to the markup: [page-number]: serious technical mistake {page-number}: minor technical mistake : important language/formatting problem (page-number): language change or minor formatting problem ?page-number?: reader question or request for clarification This page was updated June 2, 2006. UNCONFIRMED errors and comments from readers: (various) The authors use "effect" when they mean "affect." (48) 1st sentence on page. "as it does in C:" should read "as it does in C." ?68? Question 6: In the section which says, "What happens if you try to index a nonexistent key d(D['d'])?", the formatting seems to indicate that d is a function, which is having the argument D['d'] applied to it. In the next sentence, the part which says, "... nonexistent key d (e.g., D['d']=='spam')" makes use of the "e.g. to clearly differentiate between the use of the key 'd' and it's example use. The original sentence should use some text to separate the two items such as "d (e.g., D['d'])" to make it clearer. (68) Question 7, part C: 2nd sentence "How about the;" Should read "How about" {82} 4th paragraph; And finally, Python also lets you move a compound statement's body up to the header line. provided the body is just a simple --- Not only simple statements, but simple statements separated by semicolons also do it: if 1: print 1; print 'one'; print 'ogyin'; print 'egy'; print 'eins' {87} 2nd paragraph of the *first* edition; Since a prime number is "any integer greater than one if its only positive divisors (factors) are one and itself", the smallest prime number is 2 and your algorithm should not report as prime any integer less than 2. However, the algorithm published in chapter 3 indicates that certain integers less than 2 are also prime. If you were to add a test so that only values of y > 1 are reported as prime, the blemish would be fixed. The following echo indicates that the algorithm published in the first edition is incorrect.: >>> y = -2 >>> while x>1 and y > 1: if y % x == 0: print y, 'has factor', x break x = x - 1 else: print y, 'is prime' -2 is prime >>> y = 1 >>> while x>1 and y > 1: if y % x == 0: print y, 'has factor', x break x = x - 1 else: print y, 'is prime' 1 is prime (221) Table 8-2, setattr(); "creates of changes attribute" ----------^ shouldn't this be : "creates or changes attribute" ----------^ {238} 1st code example; The unpacking code example has a tuple assignment: start, stop = stop, start + struct.calcsize('B'*num_bytes) This will only work if the tuple elements get assigned sequentially (i.e., that start is updated on the RHS of the equation before the update of stop). This is not the case (thank God!). I believe the correct code is: start, stop = stop, stop + struct.calcsize('B'*num_bytes) {282} last paragraph, first sentence; COM stands for Component Object Model, not Common Object Model as it says in the book. {341-342} class Adder, __init__ definition at bottom of page and next page: The superclass Adder's __init__ method defaults the start parameter to the empty list. This is an invalid type for the subclass DictAdder. To illustrate the problem: >>> from ex6 import * >>> d = DictAdder() >>> d + {'a':1} Traceback (innermost last): File "", line 1, in ? File "ex6.py", line 7, in __add__ self.x = self.add(self.x, y) File "ex6.py", line 19, in add for k in x.keys(): AttributeError: keys [351] fix_paragraphs_with_word function; The function fix_paragraphs_with_word in the solution to Exercise 3 from Chapter 8 is wrong. Luckily it works for this particular input (pepper.txt), but in the general case it is flawed as shown in the comments below: ... omitted the correct part of the file ... def fix_paragraphs_with_word(paragraphs, word) : lenword = len(word) for par_no in range (len(paragraphs)) : p = paragraphs[par_no] wordpositions = find_indices_for(p, word) if wordpositions == [] : return <<<<<<< WRONG!!! You must not return here! Still not done with other paragraphs from the list. Should be "continue" instead of return. for start in wordpositions: # look for 'pepper' ahead indexpepper = string.find(p, 'pepper') <<<<< WRONG!!! This finds "pepper" before the word red or green. Should search from "start" only as in string.find(p, 'pepper', start) if indexpepper == -1: return -1 <<<<<< WRONG!!! Again the same blunder. You are still not done with all paragraphs. Should be "break" to exit to the outer for loop. if string.strip(p[start:indexpepper]) != " : <<< WRONG!!! There will always be something other than whitespace, namely, the word (red or green). Should be p[start+lenword:indexpepper] # something other than whitespace in between! continue where = indexpepper+len('pepper') if p[where:where+len('corn')] == 'corn' : # it's immediately followed by 'corn'! continue if string.find(p, 'salad') < where: # it's not followed by 'salad' continue # Finally! we get to do a change! p = p[:start] + 'bell' + p[start+lenword:] paragraphs [par_no] = p # change mutable argument! fix_paragraphs_with_word(paragraphs, 'red') fix_paragraphs_with_word(paragraphs, 'green') for paragraph in paragraphs: print paragraph+'\n' <<<< WRONG!!! Here you print two extra blank lines at the end of the file. One due to '\n' the other due to the print statement itself. I cannot explain so many mistakes in such a simple code except considering that too many continue and (quite wrong) return statements were applied in a small loop-in-a-loop construct. Cleaning the logic a little and applying the short-cut property of and operator in Python (just as in C, C++, Java, Perl, or Lisp) the authors could have come with the following solution: import string file = open('pepper.txt') paragraphs = string.split(file.read(), '\n\n') def find_positions(haystack, needle): positions = [] offset = 0 while 1: i = string.find(haystack, needle) if i == -1: return positions # no more matches positions.append(i + offset) haystack = haystack[i + len(needle):] offset = offset + i + len(needle) def replace_in_para_list(paragraphs, word): L = len(word) for n in range(len(paragraphs)): p = paragraphs[n] positions = find_positions(p, word) if positions != []: for pos in positions: i = string.find(p, 'pepper', pos) if i == -1: break # try next paragraph end = i + len('pepper') if (len(string.strip(p[pos+L:i])) == 0 and p[end:end+len('corn')] != 'corn' and string.find(p, 'salad', end) != -1): p = p[:pos] + 'bell' + p[pos + L:] paragraphs[n] = p replace_in_para_list(paragraphs, 'red') replace_in_para_list(paragraphs, 'green') print string.join(paragraphs, '\n\n'), [479] 2nd code section from bottom; self.__init__(self, *args, **kw) should be: Base.__init__(self, *args, **kw)