ch17: update from book draft

This commit is contained in:
Luciano Ramalho
2020-02-18 23:45:05 -03:00
parent 70650841b3
commit aa868e8f75
35 changed files with 788 additions and 31 deletions

View File

@@ -17,7 +17,8 @@ class ArithmeticProgression:
self.end = end # None -> "infinite" series
def __iter__(self):
result = type(self.begin + self.step)(self.begin)
result_type = type(self.begin + self.step)
result = result_type(self.begin)
forever = self.end is None
while forever or result < self.end:
yield result

View File

@@ -1,7 +1,7 @@
"""
Arithmetic progression class
# BEGIN ARITPROG_CLASS_DEMO
# tag::ARITPROG_CLASS_DEMO[]
>>> ap = ArithmeticProgression(0, 1, 3)
>>> list(ap)
@@ -21,24 +21,25 @@ Arithmetic progression class
>>> list(ap)
[Decimal('0.0'), Decimal('0.1'), Decimal('0.2')]
# END ARITPROG_CLASS_DEMO
# end::ARITPROG_CLASS_DEMO[]
"""
# BEGIN ARITPROG_CLASS
# tag::ARITPROG_CLASS[]
class ArithmeticProgression:
def __init__(self, begin, step, end=None): # <1>
def __init__(self, begin, step, end=None): # <1>
self.begin = begin
self.step = step
self.end = end # None -> "infinite" series
def __iter__(self):
result = type(self.begin + self.step)(self.begin) # <2>
forever = self.end is None # <3>
result_type = type(self.begin + self.step) # <2>
result = result_type(self.begin) # <3>
forever = self.end is None # <4>
index = 0
while forever or result < self.end: # <4>
yield result # <5>
while forever or result < self.end: # <5>
yield result # <6>
index += 1
result = self.begin + self.step * index # <6>
# END ARITPROG_CLASS
result = self.begin + self.step * index # <7>
# end::ARITPROG_CLASS[]

View File

@@ -19,7 +19,7 @@ Arithmetic progression generator function::
"""
# BEGIN ARITPROG_GENFUNC
# tag::ARITPROG_GENFUNC[]
def aritprog_gen(begin, step, end=None):
result = type(begin + step)(begin)
forever = end is None
@@ -28,4 +28,4 @@ def aritprog_gen(begin, step, end=None):
yield result
index += 1
result = begin + step * index
# END ARITPROG_GENFUNC
# end::ARITPROG_GENFUNC[]

View File

@@ -1,4 +1,4 @@
# BEGIN ARITPROG_ITERTOOLS
# tag::ARITPROG_ITERTOOLS[]
import itertools
@@ -8,4 +8,4 @@ def aritprog_gen(begin, step, end=None):
if end is not None:
ap_gen = itertools.takewhile(lambda n: n < end, ap_gen)
return ap_gen
# END ARITPROG_ITERTOOLS
# end::ARITPROG_ITERTOOLS[]

View File

@@ -8,7 +8,7 @@ Fibonacci generator implemented "by hand" without generator objects
"""
# BEGIN FIBO_BY_HAND
# tag::FIBO_BY_HAND[]
class Fibonacci:
def __iter__(self):
@@ -28,7 +28,7 @@ class FibonacciGenerator:
def __iter__(self):
return self
# END FIBO_BY_HAND
# end::FIBO_BY_HAND[]
# for comparison, this is the usual implementation of a Fibonacci
# generator in Python:

View File

@@ -200,11 +200,12 @@ def main(): # <4>
' for bulk insert to CouchDB via POST to db/_bulk_docs')
parser.add_argument(
'-m', '--mongo', action='store_true',
help='output individual records as separate JSON dictionaries,'
' one per line for bulk insert to MongoDB via mongoimport utility')
help='output individual records as separate JSON dictionaries, one'
' per line for bulk insert to MongoDB via mongoimport utility')
parser.add_argument(
'-t', '--type', type=int, metavar='ISIS_JSON_TYPE', default=1,
help='ISIS-JSON type, sets field structure: 1=string, 2=alist, 3=dict (default=1)')
help='ISIS-JSON type, sets field structure: 1=string, 2=alist,'
' 3=dict (default=1)')
parser.add_argument(
'-q', '--qty', type=int, default=DEFAULT_QTY,
help='maximum quantity of records to read (default=ALL)')
@@ -220,7 +221,8 @@ def main(): # <4>
help='generate an "_id" with a random UUID for each record')
parser.add_argument(
'-p', '--prefix', type=str, metavar='PREFIX', default='',
help='concatenate prefix to every numeric field tag (ex. 99 becomes "v99")')
help='concatenate prefix to every numeric field tag'
' (ex. 99 becomes "v99")')
parser.add_argument(
'-n', '--mfn', action='store_true',
help='generate an "_id" from the MFN of each record'

View File

@@ -1,7 +1,18 @@
"""
Sentence: access words by index
>>> text = 'To be, or not to be, that is the question'
>>> s = Sentence(text)
>>> len(s)
10
>>> s[1], s[5]
('be', 'be')
>>> s
Sentence('To be, or no... the question')
"""
# tag::SENTENCE_SEQ[]
import re
import reprlib
@@ -17,8 +28,10 @@ class Sentence:
def __getitem__(self, index):
return self.words[index] # <2>
def __len__(self, index): # <3>
def __len__(self): # <3>
return len(self.words)
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text) # <4>
# end::SENTENCE_SEQ[]

View File

@@ -2,6 +2,7 @@
Sentence: iterate over words using a generator function
"""
# tag::SENTENCE_GEN[]
import re
import reprlib
@@ -23,3 +24,5 @@ class Sentence:
return # <3>
# done! <4>
# end::SENTENCE_GEN[]

View File

@@ -2,10 +2,11 @@
Sentence: iterate over words using a generator function
"""
# tag::SENTENCE_GEN2[]
import re
import reprlib
RE_WORD = re.compile(r'\w+')
RE_WORD = re.compile('r\w+')
class Sentence:
@@ -19,3 +20,5 @@ class Sentence:
def __iter__(self):
for match in RE_WORD.finditer(self.text): # <2>
yield match.group() # <3>
# end::SENTENCE_GEN2[]

View File

@@ -2,7 +2,7 @@
Sentence: iterate over words using a generator expression
"""
# BEGIN SENTENCE_GENEXP
# tag::SENTENCE_GENEXP[]
import re
import reprlib
@@ -19,7 +19,7 @@ class Sentence:
def __iter__(self):
return (match.group() for match in RE_WORD.finditer(self.text))
# END SENTENCE_GENEXP
# end::SENTENCE_GENEXP[]
def main():

View File

@@ -5,7 +5,7 @@ WARNING: the Iterator Pattern is much simpler in idiomatic Python;
see: sentence_gen*.py.
"""
# BEGIN SENTENCE_ITER
# tag::SENTENCE_ITER[]
import re
import reprlib
@@ -41,7 +41,7 @@ class SentenceIterator:
def __iter__(self): # <9>
return self
# END SENTENCE_ITER
# end::SENTENCE_ITER[]
def main():
import sys

View File

@@ -0,0 +1,7 @@
def tree(cls):
yield cls.__name__
if __name__ == '__main__':
for cls_name in tree(BaseException):
print(cls_name)

View File

@@ -0,0 +1,10 @@
def tree(cls):
yield cls.__name__, 0
for sub_cls in cls.__subclasses__():
yield sub_cls.__name__, 1
if __name__ == '__main__':
for cls_name, level in tree(BaseException):
indent = ' ' * 4 * level
print(f'{indent}{cls_name}')

View File

@@ -0,0 +1,12 @@
def tree(cls):
yield cls.__name__, 0
for sub_cls in cls.__subclasses__():
yield sub_cls.__name__, 1
for sub_sub_cls in sub_cls.__subclasses__():
yield sub_sub_cls.__name__, 2
if __name__ == '__main__':
for cls_name, level in tree(BaseException):
indent = ' ' * 4 * level
print(f'{indent}{cls_name}')

View File

@@ -0,0 +1,10 @@
def tree(cls, level=0):
yield cls.__name__, level
for sub_cls in cls.__subclasses__():
yield from tree(sub_cls, level + 1)
if __name__ == '__main__':
for cls_name, level in tree(BaseException):
indent = ' ' * 4 * level
print(f'{indent}{cls_name}')

View File

@@ -0,0 +1,31 @@
from tree import tree
SPACES = ' ' * 4
HLINE = '\u2500' # ─ BOX DRAWINGS LIGHT HORIZONTAL
HLINE2 = HLINE * 2
ELBOW = f'\u2514{HLINE2} ' # └ BOX DRAWINGS LIGHT UP AND RIGHT
TEE = f'\u251C{HLINE2} ' # ├ BOX DRAWINGS LIGHT VERTICAL AND RIGHT
PIPE = f'\u2502 ' # │ BOX DRAWINGS LIGHT VERTICAL
def render_lines(tree_iter):
name, _, _ = next(tree_iter)
yield name
prefix = ''
for name, level, last in tree_iter:
if last:
connector = ELBOW
else:
connector = TEE
prefix = prefix[:4 * (level-1)]
prefix = prefix.replace(TEE, PIPE).replace(ELBOW, SPACES)
prefix += connector
yield prefix + name
if __name__ == '__main__':
for line in render_lines(tree(BaseException)):
print(line)

View File

@@ -0,0 +1,105 @@
import pytest
from pretty_tree import tree, render_lines
def test_1_level():
result = list(render_lines(tree(BrokenPipeError)))
expected = [
'BrokenPipeError',
]
assert expected == result
def test_2_levels_1_leaf():
result = list(render_lines(tree(IndentationError)))
expected = [
'IndentationError',
'└── TabError',
]
assert expected == result
def test_3_levels_1_leaf():
class X: pass
class Y(X): pass
class Z(Y): pass
result = list(render_lines(tree(X)))
expected = [
'X',
'└── Y',
' └── Z',
]
assert expected == result
def test_4_levels_1_leaf():
class Level0: pass
class Level1(Level0): pass
class Level2(Level1): pass
class Level3(Level2): pass
result = list(render_lines(tree(Level0)))
expected = [
'Level0',
'└── Level1',
' └── Level2',
' └── Level3',
]
assert expected == result
def test_2_levels_4_leaves():
class Branch: pass
class Leaf1(Branch): pass
class Leaf2(Branch): pass
class Leaf3(Branch): pass
class Leaf4(Branch): pass
result = list(render_lines(tree(Branch)))
expected = [
'Branch',
'├── Leaf1',
'├── Leaf2',
'├── Leaf3',
'└── Leaf4',
]
assert expected == result
def test_3_levels_2_leaves():
class A: pass
class B(A): pass
class C(B): pass
class D(A): pass
class E(D): pass
result = list(render_lines(tree(A)))
expected = [
'A',
'├── B',
'│ └── C',
'└── D',
' └── E',
]
assert expected == result
def test_4_levels_4_leaves():
class A: pass
class B1(A): pass
class C1(B1): pass
class D1(C1): pass
class D2(C1): pass
class C2(B1): pass
class B2(A): pass
expected = [
'A',
'├── B1',
'│ ├── C1',
'│ │ ├── D1',
'│ │ └── D2',
'│ └── C2',
'└── B2',
]
result = list(render_lines(tree(A)))
assert expected == result

View File

@@ -0,0 +1,94 @@
from tree import tree
def test_1_level():
class One: pass
expected = [('One', 0, True)]
result = list(tree(One))
assert expected == result
def test_2_levels_4_leaves():
class Branch: pass
class Leaf1(Branch): pass
class Leaf2(Branch): pass
class Leaf3(Branch): pass
class Leaf4(Branch): pass
expected = [
('Branch', 0, True),
('Leaf1', 1, False),
('Leaf2', 1, False),
('Leaf3', 1, False),
('Leaf4', 1, True),
]
result = list(tree(Branch))
assert expected == result
def test_3_levels_1_leaf():
class X: pass
class Y(X): pass
class Z(Y): pass
expected = [
('X', 0, True),
('Y', 1, True),
('Z', 2, True),
]
result = list(tree(X))
assert expected == result
def test_4_levels_1_leaf():
class Level0: pass
class Level1(Level0): pass
class Level2(Level1): pass
class Level3(Level2): pass
expected = [
('Level0', 0, True),
('Level1', 1, True),
('Level2', 2, True),
('Level3', 3, True),
]
result = list(tree(Level0))
assert expected == result
def test_4_levels_3_leaves():
class A: pass
class B1(A): pass
class C1(B1): pass
class D1(C1): pass
class B2(A): pass
class D2(C1): pass
class C2(B2): pass
expected = [
('A', 0, True),
('B1', 1, False),
('C1', 2, True),
('D1', 3, False),
('D2', 3, True),
('B2', 1, True),
('C2', 2, True),
]
result = list(tree(A))
assert expected == result
def test_many_levels_1_leaf():
class Root: pass
level_count = 100
expected = [('Root', 0, True)]
parent = Root
for level in range(1, level_count):
name = f'Sub{level}'
cls = type(name, (parent,), {})
expected.append((name, level, True))
parent = cls
result = list(tree(Root))
assert len(result) == level_count
assert result[0] == ('Root', 0, True)
assert result[-1] == ('Sub99', 99, True)
assert expected == result

View File

@@ -0,0 +1,13 @@
def tree(cls, level=0, last_in_level=True):
yield cls.__name__, level, last_in_level
subclasses = cls.__subclasses__()
if subclasses:
last = subclasses[-1]
for sub_cls in subclasses:
yield from tree(sub_cls, level+1, sub_cls is last)
if __name__ == '__main__':
for cls_name, level, _ in tree(BaseException):
indent = ' ' * 4 * level
print(f'{indent}{cls_name}')

View File

@@ -0,0 +1,8 @@
from tree import tree
def test_1_level():
class One: pass
expected = ['One']
result = list(tree(One))
assert expected == result

View File

@@ -0,0 +1,7 @@
def tree(cls):
yield cls.__name__
if __name__ == '__main__':
for cls_name in tree(BaseException):
print(cls_name)

View File

@@ -0,0 +1,25 @@
from tree import tree
def test_1_level():
class One: pass
expected = [('One', 0)]
result = list(tree(One))
assert expected == result
def test_2_levels_4_leaves():
class Branch: pass
class Leaf1(Branch): pass
class Leaf2(Branch): pass
class Leaf3(Branch): pass
class Leaf4(Branch): pass
expected = [
('Branch', 0),
('Leaf1', 1),
('Leaf2', 1),
('Leaf3', 1),
('Leaf4', 1),
]
result = list(tree(Branch))
assert expected == result

View File

@@ -0,0 +1,10 @@
def tree(cls):
yield cls.__name__, 0 # <1>
for sub_cls in cls.__subclasses__(): # <2>
yield sub_cls.__name__, 1 # <3>
if __name__ == '__main__':
for cls_name, level in tree(BaseException):
indent = ' ' * 4 * level # <4>
print(f'{indent}{cls_name}')

View File

@@ -0,0 +1,25 @@
from tree import tree
def test_1_level():
class One: pass
expected = [('One', 0)]
result = list(tree(One))
assert expected == result
def test_2_levels_4_leaves():
class Branch: pass
class Leaf1(Branch): pass
class Leaf2(Branch): pass
class Leaf3(Branch): pass
class Leaf4(Branch): pass
expected = [
('Branch', 0),
('Leaf1', 1),
('Leaf2', 1),
('Leaf3', 1),
('Leaf4', 1),
]
result = list(tree(Branch))
assert expected == result

View File

@@ -0,0 +1,14 @@
def tree(cls):
yield cls.__name__, 0
yield from sub_tree(cls) # <1>
def sub_tree(cls):
for sub_cls in cls.__subclasses__():
yield sub_cls.__name__, 1 # <2>
if __name__ == '__main__':
for cls_name, level in tree(BaseException):
indent = ' ' * 4 * level
print(f'{indent}{cls_name}')

View File

@@ -0,0 +1,38 @@
from tree import tree
def test_1_level():
class One: pass
expected = [('One', 0)]
result = list(tree(One))
assert expected == result
def test_2_levels_4_leaves():
class Branch: pass
class Leaf1(Branch): pass
class Leaf2(Branch): pass
class Leaf3(Branch): pass
class Leaf4(Branch): pass
expected = [
('Branch', 0),
('Leaf1', 1),
('Leaf2', 1),
('Leaf3', 1),
('Leaf4', 1),
]
result = list(tree(Branch))
assert expected == result
def test_3_levels_1_leaf():
class X: pass
class Y(X): pass
class Z(Y): pass
expected = [
('X', 0),
('Y', 1),
('Z', 2),
]
result = list(tree(X))
assert expected == result

View File

@@ -0,0 +1,16 @@
def tree(cls):
yield cls.__name__, 0
yield from sub_tree(cls)
def sub_tree(cls):
for sub_cls in cls.__subclasses__():
yield sub_cls.__name__, 1
for sub_sub_cls in sub_cls.__subclasses__():
yield sub_sub_cls.__name__, 2
if __name__ == '__main__':
for cls_name, level in tree(BaseException):
indent = ' ' * 4 * level
print(f'{indent}{cls_name}')

View File

@@ -0,0 +1,76 @@
from tree import tree
def test_1_level():
class One: pass
expected = [('One', 0)]
result = list(tree(One))
assert expected == result
def test_2_levels_4_leaves():
class Branch: pass
class Leaf1(Branch): pass
class Leaf2(Branch): pass
class Leaf3(Branch): pass
class Leaf4(Branch): pass
expected = [
('Branch', 0),
('Leaf1', 1),
('Leaf2', 1),
('Leaf3', 1),
('Leaf4', 1),
]
result = list(tree(Branch))
assert expected == result
def test_3_levels_1_leaf():
class X: pass
class Y(X): pass
class Z(Y): pass
expected = [
('X', 0),
('Y', 1),
('Z', 2),
]
result = list(tree(X))
assert expected == result
def test_4_levels_1_leaf():
class Level0: pass
class Level1(Level0): pass
class Level2(Level1): pass
class Level3(Level2): pass
expected = [
('Level0', 0),
('Level1', 1),
('Level2', 2),
('Level3', 3),
]
result = list(tree(Level0))
assert expected == result
def test_4_levels_3_leaves():
class A: pass
class B1(A): pass
class C1(B1): pass
class D1(C1): pass
class B2(A): pass
class D2(C1): pass
class C2(B2): pass
expected = [
('A', 0),
('B1', 1),
('C1', 2),
('D1', 3),
('D2', 3),
('B2', 1),
('C2', 2),
]
result = list(tree(A))
assert expected == result

View File

@@ -0,0 +1,20 @@
def tree(cls):
yield cls.__name__, 0
yield from sub_tree(cls)
# tag::SUB_TREE[]
def sub_tree(cls):
for sub_cls in cls.__subclasses__():
yield sub_cls.__name__, 1
for sub_sub_cls in sub_cls.__subclasses__():
yield sub_sub_cls.__name__, 2
for sub_sub_sub_cls in sub_sub_cls.__subclasses__():
yield sub_sub_sub_cls.__name__, 3
# end::SUB_TREE[]
if __name__ == '__main__':
for cls_name, level in tree(BaseException):
indent = ' ' * 4 * level
print(f'{indent}{cls_name}')

View File

@@ -0,0 +1,94 @@
from tree import tree
def test_1_level():
class One: pass
expected = [('One', 0)]
result = list(tree(One))
assert expected == result
def test_2_levels_4_leaves():
class Branch: pass
class Leaf1(Branch): pass
class Leaf2(Branch): pass
class Leaf3(Branch): pass
class Leaf4(Branch): pass
expected = [
('Branch', 0),
('Leaf1', 1),
('Leaf2', 1),
('Leaf3', 1),
('Leaf4', 1),
]
result = list(tree(Branch))
assert expected == result
def test_3_levels_1_leaf():
class X: pass
class Y(X): pass
class Z(Y): pass
expected = [
('X', 0),
('Y', 1),
('Z', 2),
]
result = list(tree(X))
assert expected == result
def test_4_levels_1_leaf():
class Level0: pass
class Level1(Level0): pass
class Level2(Level1): pass
class Level3(Level2): pass
expected = [
('Level0', 0),
('Level1', 1),
('Level2', 2),
('Level3', 3),
]
result = list(tree(Level0))
assert expected == result
def test_4_levels_3_leaves():
class A: pass
class B1(A): pass
class C1(B1): pass
class D1(C1): pass
class B2(A): pass
class D2(C1): pass
class C2(B2): pass
expected = [
('A', 0),
('B1', 1),
('C1', 2),
('D1', 3),
('D2', 3),
('B2', 1),
('C2', 2),
]
result = list(tree(A))
assert expected == result
def test_many_levels_1_leaf():
class Root: pass
level_count = 100
expected = [('Root', 0)]
parent = Root
for level in range(1, level_count):
name = f'Sub{level}'
cls = type(name, (parent,), {})
expected.append((name, level))
parent = cls
result = list(tree(Root))
assert len(result) == level_count
assert result[0] == ('Root', 0)
assert result[-1] == ('Sub99', 99)
assert expected == result

View File

@@ -0,0 +1,15 @@
def tree(cls):
yield cls.__name__, 0
yield from sub_tree(cls, 1)
def sub_tree(cls, level):
for sub_cls in cls.__subclasses__():
yield sub_cls.__name__, level
yield from sub_tree(sub_cls, level+1)
if __name__ == '__main__':
for cls_name, level in tree(BaseException):
indent = ' ' * 4 * level
print(f'{indent}{cls_name}')

View File

@@ -0,0 +1,94 @@
from tree import tree
def test_1_level():
class One: pass
expected = [('One', 0)]
result = list(tree(One))
assert expected == result
def test_2_levels_4_leaves():
class Branch: pass
class Leaf1(Branch): pass
class Leaf2(Branch): pass
class Leaf3(Branch): pass
class Leaf4(Branch): pass
expected = [
('Branch', 0),
('Leaf1', 1),
('Leaf2', 1),
('Leaf3', 1),
('Leaf4', 1),
]
result = list(tree(Branch))
assert expected == result
def test_3_levels_1_leaf():
class X: pass
class Y(X): pass
class Z(Y): pass
expected = [
('X', 0),
('Y', 1),
('Z', 2),
]
result = list(tree(X))
assert expected == result
def test_4_levels_1_leaf():
class Level0: pass
class Level1(Level0): pass
class Level2(Level1): pass
class Level3(Level2): pass
expected = [
('Level0', 0),
('Level1', 1),
('Level2', 2),
('Level3', 3),
]
result = list(tree(Level0))
assert expected == result
def test_4_levels_3_leaves():
class A: pass
class B1(A): pass
class C1(B1): pass
class D1(C1): pass
class B2(A): pass
class D2(C1): pass
class C2(B2): pass
expected = [
('A', 0),
('B1', 1),
('C1', 2),
('D1', 3),
('D2', 3),
('B2', 1),
('C2', 2),
]
result = list(tree(A))
assert expected == result
def test_many_levels_1_leaf():
class Root: pass
level_count = 100
expected = [('Root', 0)]
parent = Root
for level in range(1, level_count):
name = f'Sub{level}'
cls = type(name, (parent,), {})
expected.append((name, level))
parent = cls
result = list(tree(Root))
assert len(result) == level_count
assert result[0] == ('Root', 0)
assert result[-1] == ('Sub99', 99)
assert expected == result

View File

@@ -0,0 +1,10 @@
def tree(cls, level=0):
yield cls.__name__, level
for sub_cls in cls.__subclasses__():
yield from tree(sub_cls, level+1)
if __name__ == '__main__':
for cls_name, level in tree(BaseException):
indent = ' ' * 4 * level
print(f'{indent}{cls_name}')

View File

@@ -14,7 +14,7 @@ Daniel Patterson, Junsong Li, Anand Chitipothu, and Shriram Krishnamurthi.
DOI=10.1145/2544173.2509536 http://doi.acm.org/10.1145/2544173.2509536
"""
# BEGIN YIELD_DELEGATE_FAIL
# tag::YIELD_DELEGATE_FAIL[]
def f():
def do_yield(n):
yield n
@@ -22,7 +22,7 @@ def f():
while True:
x += 1
do_yield(x)
# END YIELD_DELEGATE_FAIL
# end::YIELD_DELEGATE_FAIL[]
if __name__ == '__main__':
print('Invoking f() results in an infinite loop')

View File

@@ -5,7 +5,7 @@ yielding.
"""
# BEGIN YIELD_DELEGATE_FIX
# tag::YIELD_DELEGATE_FIX[]
def f():
def do_yield(n):
yield n
@@ -13,7 +13,7 @@ def f():
while True:
x += 1
yield from do_yield(x)
# END YIELD_DELEGATE_FIX
# end::YIELD_DELEGATE_FIX[]
if __name__ == '__main__':
print('Invoking f() now produces a generator')