Skip to content

Commit 661b68b

Browse files
committed
updates for chapter 11
1 parent ca049e9 commit 661b68b

File tree

8 files changed

+476
-30
lines changed

8 files changed

+476
-30
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,6 @@ docs/_build/
5555

5656
# PyBuilder
5757
target/
58+
59+
# PyCharm
60+
.idea/

classes/sum-nth-element.rst

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
======================================
2+
Pythonic way to sum n-th list element?
3+
======================================
4+
5+
Examples inspired by Guy Middleton's question on Python-list, Fri Apr 18 22:21:08 CEST 2003. Message: https://mail.python.org/pipermail/python-list/2003-April/218568.html
6+
7+
Guy Middleton::
8+
9+
>>> my_list = [[1, 2, 3], [40, 50, 60], [9, 8, 7]]
10+
>>> import functools as ft
11+
>>> ft.reduce(lambda a, b: a+b, [sub[1] for sub in my_list])
12+
60
13+
14+
LR::
15+
16+
>>> ft.reduce(lambda a, b: a + b[1], my_list, 0)
17+
60
18+
19+
Fernando Perez::
20+
21+
>>> import numpy as np
22+
>>> my_array = np.array(my_list)
23+
>>> np.sum(my_array[:, 1])
24+
60
25+
26+
Skip Montanaro::
27+
28+
>>> import operator
29+
>>> ft.reduce(operator.add, [sub[1] for sub in my_list], 0)
30+
60
31+
>>> ft.reduce(operator.add, [sub[1] for sub in []])
32+
Traceback (most recent call last):
33+
...
34+
TypeError: reduce() of empty sequence with no initial value
35+
>>> ft.reduce(operator.add, [sub[1] for sub in []], 0)
36+
0
37+
38+
39+
Evan Simpson::
40+
41+
>>> total = 0
42+
>>> for sub in my_list:
43+
... total += sub[1]
44+
>>> total
45+
60
46+
47+
Alex Martelli (``sum`` was added in Python 2.3, released July 9, 2003)::
48+
49+
>>> sum([sub[1] for sub in my_list])
50+
60
51+
52+
After generator expressions (added in Python 2.4, November 30, 2004)::
53+
54+
>>> sum(sub[1] for sub in my_list)
55+
60
56+
57+
If you want the sum of a list of items, you should write it in a way
58+
that looks like "the sum of a list of items", not in a way that looks
59+
like "loop over these items, maintain another variable t, perform a
60+
sequence of additions". Why do we have high level languages if not to
61+
express our intentions at a higher level and let the language worry
62+
about what low-level operations are needed to implement it?
63+
64+
David Eppstein
65+
66+
Alex Martelli
67+
68+
https://mail.python.org/pipermail/python-list/2003-April/186311.html
69+
70+
"The sum" is so frequently needed that I wouldn't mind at all if
71+
Python singled it out as a built-in. But "reduce(operator.add, ..."
72+
just isn't a great way to express it, in my opinion (and yet as an
73+
old APL'er, and FP-liker, I _should_ like it -- but I don't).
74+
75+
https://mail.python.org/pipermail/python-list/2003-April/225323.html
76+
77+
Four years later, having coded a lot of Python, taught it widely,
78+
written a lot about it, and so on, I've changed my mind: I now
79+
think that reduce is more trouble than it's worth and Python
80+
would be better off without it, if it was being designed from
81+
scratch today -- it would not substantially reduce (:-) Python's
82+
power and WOULD substantially ease the teaching/&c task. That's
83+
not a strong-enough argument to REMOVE a builtin, of course, and
84+
thus that's definitely NOT what I'm arguing for. But I do suggest
85+
avoiding reduce in most cases -- that's all.

classes/vector_v1.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
(3.0, 4.0)
2727
>>> octets = bytes(v1)
2828
>>> octets
29-
b'\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x10@'
29+
b'd\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x10@'
3030
>>> abs(v1)
3131
5.0
3232
>>> bool(v1), bool(Vector([0, 0]))
@@ -106,7 +106,8 @@ def __str__(self):
106106
return str(tuple(self))
107107

108108
def __bytes__(self):
109-
return bytes(self._components) # <5>
109+
return (bytes([ord(self.typecode)]) +
110+
bytes(self._components)) # <5>
110111

111112
def __eq__(self, other):
112113
return tuple(self) == tuple(other)
@@ -119,6 +120,7 @@ def __bool__(self):
119120

120121
@classmethod
121122
def frombytes(cls, octets):
122-
memv = memoryview(octets).cast(cls.typecode)
123+
typecode = chr(octets[0])
124+
memv = memoryview(octets[1:]).cast(typecode)
123125
return cls(memv) # <7>
124126
# END VECTOR_V1

classes/vector_v2.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
(3.0, 4.0)
2727
>>> octets = bytes(v1)
2828
>>> octets
29-
b'\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x10@'
29+
b'd\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x10@'
3030
>>> abs(v1)
3131
5.0
3232
>>> bool(v1), bool(Vector([0, 0]))
@@ -132,7 +132,8 @@ def __str__(self):
132132
return str(tuple(self))
133133

134134
def __bytes__(self):
135-
return bytes(self._components)
135+
return (bytes([ord(self.typecode)]) +
136+
bytes(self._components))
136137

137138
def __eq__(self, other):
138139
return tuple(self) == tuple(other)
@@ -160,5 +161,6 @@ def __getitem__(self, index):
160161

161162
@classmethod
162163
def frombytes(cls, octets):
163-
memv = memoryview(octets).cast(cls.typecode)
164+
typecode = chr(octets[0])
165+
memv = memoryview(octets[1:]).cast(typecode)
164166
return cls(memv)

classes/vector_v3.py

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
(3.0, 4.0)
2727
>>> octets = bytes(v1)
2828
>>> octets
29-
b'\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x10@'
29+
b'd\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x10@'
3030
>>> abs(v1)
3131
5.0
3232
>>> bool(v1), bool(Vector([0, 0]))
@@ -109,8 +109,9 @@
109109
>>> v7 = Vector(range(10))
110110
>>> v7.x
111111
0.0
112-
>>> v7.y, v7.z, v7.t, v7.u, v7.v, v7.w
113-
(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)
112+
>>> v7.y, v7.z, v7.t
113+
(1.0, 2.0, 3.0)
114+
114115
115116
Dynamic attribute lookup failures::
116117
@@ -129,6 +130,26 @@
129130
AttributeError: 'Vector' object has no attribute 'spam'
130131
131132
133+
Tests of preventing attributes from 'a' to 'z'::
134+
135+
>>> v1.x = 7
136+
Traceback (most recent call last):
137+
...
138+
AttributeError: readonly attribute 'x'
139+
>>> v1.w = 7
140+
Traceback (most recent call last):
141+
...
142+
AttributeError: can't set attributes 'a' to 'z' in 'Vector'
143+
144+
Other attributes can be set::
145+
146+
>>> v1.X = 'albatross'
147+
>>> v1.X
148+
'albatross'
149+
>>> v1.ni = 'Ni!'
150+
>>> v1.ni
151+
'Ni!'
152+
132153
"""
133154

134155
from array import array
@@ -154,7 +175,8 @@ def __str__(self):
154175
return str(tuple(self))
155176

156177
def __bytes__(self):
157-
return bytes(self._components)
178+
return (bytes([ord(self.typecode)]) +
179+
bytes(self._components))
158180

159181
def __eq__(self, other):
160182
return tuple(self) == tuple(other)
@@ -178,8 +200,8 @@ def __getitem__(self, index):
178200
msg = '{.__name__} indices must be integers'
179201
raise TypeError(msg.format(cls))
180202

181-
# BEGIN VECTOR_V3
182-
shortcut_names = 'xyztuvw'
203+
# BEGIN VECTOR_V3_GETATTR
204+
shortcut_names = 'xyzt'
183205

184206
def __getattr__(self, name):
185207
cls = type(self) # <1>
@@ -190,9 +212,27 @@ def __getattr__(self, name):
190212
msg = '{.__name__!r} object has no attribute {!r}' # <5>
191213
raise AttributeError(msg.format(cls, name))
192214

193-
# END VECTOR_V3
215+
# END VECTOR_V3_GETATTR
216+
# BEGIN VECTOR_V3_SETATTR
217+
def __setattr__(self, name, value):
218+
cls = type(self)
219+
if len(name) == 1: # <1>
220+
if name in cls.shortcut_names: # <2>
221+
error = 'readonly attribute {attr_name!r}'
222+
elif name.islower(): # <3>
223+
error = "can't set attributes 'a' to 'z' in {cls_name!r}"
224+
else:
225+
error = '' # <4>
226+
if error: # <5>
227+
msg = error.format(cls_name=cls.__name__, attr_name=name)
228+
raise AttributeError(msg)
229+
super().__setattr__(name, value) # <6>
230+
231+
# END VECTOR_V3_SETATTR
232+
194233

195234
@classmethod
196235
def frombytes(cls, octets):
197-
memv = memoryview(octets).cast(cls.typecode)
236+
typecode = chr(octets[0])
237+
memv = memoryview(octets[1:]).cast(typecode)
198238
return cls(memv)

classes/vector_v4.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
(3.0, 4.0)
2727
>>> octets = bytes(v1)
2828
>>> octets
29-
b'\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x10@'
29+
b'd\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x10@'
3030
>>> abs(v1)
3131
5.0
3232
>>> bool(v1), bool(Vector([0, 0]))
@@ -109,8 +109,8 @@
109109
>>> v7 = Vector(range(10))
110110
>>> v7.x
111111
0.0
112-
>>> v7.y, v7.z, v7.t, v7.u, v7.v, v7.w
113-
(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)
112+
>>> v7.y, v7.z, v7.t
113+
(1.0, 2.0, 3.0)
114114
115115
Dynamic attribute lookup failures::
116116
@@ -168,14 +168,16 @@ def __str__(self):
168168
return str(tuple(self))
169169

170170
def __bytes__(self):
171-
return bytes(self._components)
171+
return (bytes([ord(self.typecode)]) +
172+
bytes(self._components))
172173

173174
def __eq__(self, other):
174-
return tuple(self) == tuple(other)
175+
return (len(self) == len(other) and
176+
all(a == b for a, b in zip(self, other)))
175177

176178
def __hash__(self):
177179
hashes = (hash(x) for x in self)
178-
return functools.reduce(operator.xor, hashes)
180+
return functools.reduce(operator.xor, hashes, 0)
179181

180182
def __abs__(self):
181183
return math.sqrt(sum(x * x for x in self))
@@ -196,7 +198,7 @@ def __getitem__(self, index):
196198
msg = '{.__name__} indices must be integers'
197199
raise TypeError(msg.format(cls))
198200

199-
shortcut_names = 'xyztuvw'
201+
shortcut_names = 'xyzt'
200202

201203
def __getattr__(self, name):
202204
cls = type(self) # <1>
@@ -209,5 +211,6 @@ def __getattr__(self, name):
209211

210212
@classmethod
211213
def frombytes(cls, octets):
212-
memv = memoryview(octets).cast(cls.typecode)
214+
typecode = chr(octets[0])
215+
memv = memoryview(octets[1:]).cast(typecode)
213216
return cls(memv)

classes/vector_v5.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
(3.0, 4.0)
2828
>>> octets = bytes(v1)
2929
>>> octets
30-
b'\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x10@'
30+
b'd\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x10@'
3131
>>> abs(v1)
3232
5.0
3333
>>> bool(v1), bool(Vector([0, 0]))
@@ -110,8 +110,8 @@
110110
>>> v7 = Vector(range(10))
111111
>>> v7.x
112112
0.0
113-
>>> v7.y, v7.z, v7.t, v7.u, v7.v, v7.w
114-
(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)
113+
>>> v7.y, v7.z, v7.t
114+
(1.0, 2.0, 3.0)
115115
116116
Dynamic attribute lookup failures::
117117
@@ -210,14 +210,16 @@ def __str__(self):
210210
return str(tuple(self))
211211

212212
def __bytes__(self):
213-
return bytes(self._components)
213+
return (bytes([ord(self.typecode)]) +
214+
bytes(self._components))
214215

215216
def __eq__(self, other):
216-
return tuple(self) == tuple(other)
217+
return (len(self) == len(other) and
218+
all(a == b for a, b in zip(self, other)))
217219

218220
def __hash__(self):
219221
hashes = (hash(x) for x in self)
220-
return functools.reduce(operator.xor, hashes)
222+
return functools.reduce(operator.xor, hashes, 0)
221223

222224
def __abs__(self):
223225
return math.sqrt(sum(x * x for x in self))
@@ -238,7 +240,7 @@ def __getitem__(self, index):
238240
msg = '{.__name__} indices must be integers'
239241
raise TypeError(msg.format(cls))
240242

241-
shortcut_names = 'xyztuvw'
243+
shortcut_names = 'xyzt'
242244

243245
def __getattr__(self, name):
244246
cls = type(self)
@@ -274,6 +276,7 @@ def __format__(self, fmt_spec=''):
274276

275277
@classmethod
276278
def frombytes(cls, octets):
277-
memv = memoryview(octets).cast(cls.typecode)
279+
typecode = chr(octets[0])
280+
memv = memoryview(octets[1:]).cast(typecode)
278281
return cls(memv)
279282
# END VECTOR_V5

0 commit comments

Comments
 (0)