Learning Python 4th Edition: First Printing Clarifications (original) (raw)

Below are book clarifications applied in the first reprint of this book, dated January 2010. Their suggested updates are not present in paper copies of the book's first printing (September 2009).












L = [x for x in 'spam']
L
['s', 'p', 'a', 'm']
x
NameError: name 'x' is not defined

G = (y for y in 'spam')
G
<generator object at 0x02501D00>
y
NameError: name 'y' is not defined

{x for x in 'spam'}
{'a', 'p', 's', 'm'}
x
NameError: name 'x' is not defined

{x: x*2 for x in 'spam'}
{'a': 'aa', 'p': 'pp', 's': 'ss', 'm': 'mm'}
x
NameError: name 'x' is not defined

for z in 'spam': print(z, end=' ')
...
s p a m >>> z
'm'
In Python 2.6, list comprehensions and generator expressions are supported, but the former does not localize its loop variables -- they retain their last iteration value, just like for-loop statements:
# Python 2.6
L = [x for x in 'spam']
x <=== DIFFERS
'm'

G = (y for y in 'spam')
y
NameError: name 'y' is not defined

for z in 'spam': print z,
...
s p a m
z
'm'
To clarify, in future printings of the book we'll add the following text as a footnote at the bottom of page 413, with its star at the very end of the sentence just before the note box on that page [reprints: please ask me how to shorten this if it does not fit on that page as is]: """There is technically one more scope in Python: loop variables in comprehension and generator expressions are local to the expression itself in 3.X (in 2.X, they are local in generators but not in list comprehensions). This is a special and obscure case that rarely impacts real code, and differs from for-loop statements which never localize their variables.""".


def tester(start):
... def nested(label):
... print(label, nested.state)
... nested.state += 1 # changes object, not name
... nested.state = start # per-call state is retained:
... return nested # each tester() makes a new func
...
F = tester(0)
F('spam')
spam 0
F('eggs')
eggs 1

G = tester(42) # each nested func object has its own state
G('ham')
ham 42
G('bacon')
bacon 43

F('sausage')
sausage 2
F.state, G.state # and state is accessible from outside
(3, 44)
Interestingly, you can achieve the same effect with a reference to a mutable object in the enclosing scope instead of an attribute attached to the function itself, albeit at some arguable cost in code simplicity. The following alternative, not listed in the book, works similarly because each call to the outer function gets a new local scope; unlike function attributes, though, the state is not directly visible outside the function:
# Python 2.X and 3.X: change mutable in enclosing scope (not in book)
def tester(start):
... def nested(label):
... print(label, state[0])
... state[0] += 1 # changes object, not name
... state = [start]
... return nested
...
F = tester(0) # per-call state retained again:
F('spam') # each tester() has new local scope
('spam', 0)
F('eggs') # but no F.state accessible here
('eggs', 1)

G = tester(42)
G('ham')
('ham', 42)
G('bacon')
('bacon', 43)

F('sausage')
('sausage', 2)
Of course, as shown in the book, the 3.X "nonlocal" statement offers an alternative that is often seen as more straightforward than both the above schemes; its only potential downside is that unlike function attributes, the state it retains is not directly visible outside the function (which may matter in some contexts):
# Python 3.X only: nonlocal statement (in book)
def tester(start):
... state = start
... def nested(label):
... nonlocal state # change enclosing scope name
... print(label, state) # but state not visible outside
... state += 1
... return nested
...
F = tester(0)
F('spam')
spam 0
G = tester(42)
G('eggs')
eggs 42
F('eggs')
eggs 1
F.state
AttributeError: 'function' object has no attribute 'state'
Also see the further coverage of function attributes in page 471; this probably should be noted at the earlier page 431 example, along with the fact that both 2.6 and 3.X support function attributes, and the rehash of these techniques in the decorators chapter. To clarify all this a bit in future printings, make two changes:

  1. Add a footnote star at the very end of the first paragraph on page 432, referencing a new footnote on page 432 that has the following text: """Function attributes are supported in both Python 2.6 and 3.X. We'll explore them further in Chapter 19, and revisit all the state options introduced here in Chapter 38 in a more realistic context. Also note that it's possible to change a mutable object in the enclosing scope in 2.X and 3.X without declaring its name nonlocal (e.g, state=[start] in tester, state[0]+=1 in nested), though it's perhaps more obscure than either function attributes or 3.X's nonlocal.""". Within this, "state=[start]", "tester", "state[0]+=1", and "nested" should all be in fixed-width font.
  2. Add page 471 to the index entry for "function attributes" (though it's already mentioned in function/attributes and annotations).

[Reprints: please let me know if these two changes need clarification or trimming.]







S = 'A\xC4B\xE8C'
S
'A\xc4B\xe8C'
print S
AÄBèC

U = u'A\xC4B\xE8C'
U
u'A\xc4B\xe8C'
print U <=== print in 2.X to show chars
AÄBèC
# Python 2.6, in a Windows Command Prompt (DOS shell)
S = 'A\xC4B\xE8C'
S
'A\xc4B\xe8C'
print S
A─BΦC <=== DIFFERS from IDLE display

U = u'A\xC4B\xE8C'
U
u'A\xc4B\xe8C'
print U
AÄBèC
# Python 3.1, in both interfaces (IDLE, DOS shell)
S = 'A\xC4B\xE8C'
S <=== no need to print() in 3.X
'AÄBèC'
print(S)
AÄBèC

B = b'A\xC4B\xE8C'
B
b'A\xc4B\xe8C'
print(B)
b'A\xc4B\xe8C'


  1. For one, a prime use case for managed attributes is adding _accessor functions_after the fact -- code that controls attribute access that isn't required in Python, but often recommended. I'm not going to change the book for this, because this role is strongly implied by the introduction to Chapter 37 (if not mentioned by name there), and the notion of formal "accessors" is more important in other languages.
  2. For another, Python's function decorators map closely to what some other language communities call aspects -- code run before or after a function call. I'm not going to patch the book for this either, because the text does mention aspect-oriented programming in relation to metaclasses in Chapter 39 (tough not for decorators in Chapter 38), and as a rule tries to avoid descending into alternative programming patterns promoted by other languages.

If you think in terms of "accessors" and "aspects", Python provides the support you are accustomed to, though in Python concepts in other languages are often just specific use cases of more general tools.

len(open('README.txt').read())
7013
# shown: reads one line at a time with file iterators
tot = 0
for line in open('README.txt'): tot += len(line)
tot
7013
# not shown: generator expression -- reads one line at a time, sums along the way
sum(len(line) for line in open('README.txt'))
7013
# other part: using binary mode to match system size (keep Windows \r\n)

sum(len(line) for line in open('README.txt', 'rb'))
7216
import os
os.path.getsize('README.txt')
7216
See Chapter 20 for more on generator expressions, and Chapters 4, 5, and 14 for sum(). sum() allows any iterable, and both the file iterator and generator expression in this alternative solution defer file line fetches until requested. The effect is to avoid an explicit for loop, and perhaps speed performance.