ch17: update from book draft
This commit is contained in:
@@ -17,7 +17,8 @@ class ArithmeticProgression:
|
|||||||
self.end = end # None -> "infinite" series
|
self.end = end # None -> "infinite" series
|
||||||
|
|
||||||
def __iter__(self):
|
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
|
forever = self.end is None
|
||||||
while forever or result < self.end:
|
while forever or result < self.end:
|
||||||
yield result
|
yield result
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
Arithmetic progression class
|
Arithmetic progression class
|
||||||
|
|
||||||
# BEGIN ARITPROG_CLASS_DEMO
|
# tag::ARITPROG_CLASS_DEMO[]
|
||||||
|
|
||||||
>>> ap = ArithmeticProgression(0, 1, 3)
|
>>> ap = ArithmeticProgression(0, 1, 3)
|
||||||
>>> list(ap)
|
>>> list(ap)
|
||||||
@@ -21,24 +21,25 @@ Arithmetic progression class
|
|||||||
>>> list(ap)
|
>>> list(ap)
|
||||||
[Decimal('0.0'), Decimal('0.1'), Decimal('0.2')]
|
[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:
|
class ArithmeticProgression:
|
||||||
|
|
||||||
def __init__(self, begin, step, end=None): # <1>
|
def __init__(self, begin, step, end=None): # <1>
|
||||||
self.begin = begin
|
self.begin = begin
|
||||||
self.step = step
|
self.step = step
|
||||||
self.end = end # None -> "infinite" series
|
self.end = end # None -> "infinite" series
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
result = type(self.begin + self.step)(self.begin) # <2>
|
result_type = type(self.begin + self.step) # <2>
|
||||||
forever = self.end is None # <3>
|
result = result_type(self.begin) # <3>
|
||||||
|
forever = self.end is None # <4>
|
||||||
index = 0
|
index = 0
|
||||||
while forever or result < self.end: # <4>
|
while forever or result < self.end: # <5>
|
||||||
yield result # <5>
|
yield result # <6>
|
||||||
index += 1
|
index += 1
|
||||||
result = self.begin + self.step * index # <6>
|
result = self.begin + self.step * index # <7>
|
||||||
# END ARITPROG_CLASS
|
# end::ARITPROG_CLASS[]
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ Arithmetic progression generator function::
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
# BEGIN ARITPROG_GENFUNC
|
# tag::ARITPROG_GENFUNC[]
|
||||||
def aritprog_gen(begin, step, end=None):
|
def aritprog_gen(begin, step, end=None):
|
||||||
result = type(begin + step)(begin)
|
result = type(begin + step)(begin)
|
||||||
forever = end is None
|
forever = end is None
|
||||||
@@ -28,4 +28,4 @@ def aritprog_gen(begin, step, end=None):
|
|||||||
yield result
|
yield result
|
||||||
index += 1
|
index += 1
|
||||||
result = begin + step * index
|
result = begin + step * index
|
||||||
# END ARITPROG_GENFUNC
|
# end::ARITPROG_GENFUNC[]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# BEGIN ARITPROG_ITERTOOLS
|
# tag::ARITPROG_ITERTOOLS[]
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
|
|
||||||
@@ -8,4 +8,4 @@ def aritprog_gen(begin, step, end=None):
|
|||||||
if end is not None:
|
if end is not None:
|
||||||
ap_gen = itertools.takewhile(lambda n: n < end, ap_gen)
|
ap_gen = itertools.takewhile(lambda n: n < end, ap_gen)
|
||||||
return ap_gen
|
return ap_gen
|
||||||
# END ARITPROG_ITERTOOLS
|
# end::ARITPROG_ITERTOOLS[]
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ Fibonacci generator implemented "by hand" without generator objects
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
# BEGIN FIBO_BY_HAND
|
# tag::FIBO_BY_HAND[]
|
||||||
class Fibonacci:
|
class Fibonacci:
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
@@ -28,7 +28,7 @@ class FibonacciGenerator:
|
|||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return self
|
return self
|
||||||
# END FIBO_BY_HAND
|
# end::FIBO_BY_HAND[]
|
||||||
|
|
||||||
# for comparison, this is the usual implementation of a Fibonacci
|
# for comparison, this is the usual implementation of a Fibonacci
|
||||||
# generator in Python:
|
# generator in Python:
|
||||||
|
|||||||
@@ -200,11 +200,12 @@ def main(): # <4>
|
|||||||
' for bulk insert to CouchDB via POST to db/_bulk_docs')
|
' for bulk insert to CouchDB via POST to db/_bulk_docs')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-m', '--mongo', action='store_true',
|
'-m', '--mongo', action='store_true',
|
||||||
help='output individual records as separate JSON dictionaries,'
|
help='output individual records as separate JSON dictionaries, one'
|
||||||
' one per line for bulk insert to MongoDB via mongoimport utility')
|
' per line for bulk insert to MongoDB via mongoimport utility')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-t', '--type', type=int, metavar='ISIS_JSON_TYPE', default=1,
|
'-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(
|
parser.add_argument(
|
||||||
'-q', '--qty', type=int, default=DEFAULT_QTY,
|
'-q', '--qty', type=int, default=DEFAULT_QTY,
|
||||||
help='maximum quantity of records to read (default=ALL)')
|
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')
|
help='generate an "_id" with a random UUID for each record')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-p', '--prefix', type=str, metavar='PREFIX', default='',
|
'-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(
|
parser.add_argument(
|
||||||
'-n', '--mfn', action='store_true',
|
'-n', '--mfn', action='store_true',
|
||||||
help='generate an "_id" from the MFN of each record'
|
help='generate an "_id" from the MFN of each record'
|
||||||
|
|||||||
@@ -1,7 +1,18 @@
|
|||||||
"""
|
"""
|
||||||
Sentence: access words by index
|
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 re
|
||||||
import reprlib
|
import reprlib
|
||||||
|
|
||||||
@@ -17,8 +28,10 @@ class Sentence:
|
|||||||
def __getitem__(self, index):
|
def __getitem__(self, index):
|
||||||
return self.words[index] # <2>
|
return self.words[index] # <2>
|
||||||
|
|
||||||
def __len__(self, index): # <3>
|
def __len__(self): # <3>
|
||||||
return len(self.words)
|
return len(self.words)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return 'Sentence(%s)' % reprlib.repr(self.text) # <4>
|
return 'Sentence(%s)' % reprlib.repr(self.text) # <4>
|
||||||
|
|
||||||
|
# end::SENTENCE_SEQ[]
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
Sentence: iterate over words using a generator function
|
Sentence: iterate over words using a generator function
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# tag::SENTENCE_GEN[]
|
||||||
import re
|
import re
|
||||||
import reprlib
|
import reprlib
|
||||||
|
|
||||||
@@ -23,3 +24,5 @@ class Sentence:
|
|||||||
return # <3>
|
return # <3>
|
||||||
|
|
||||||
# done! <4>
|
# done! <4>
|
||||||
|
|
||||||
|
# end::SENTENCE_GEN[]
|
||||||
|
|||||||
@@ -2,10 +2,11 @@
|
|||||||
Sentence: iterate over words using a generator function
|
Sentence: iterate over words using a generator function
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# tag::SENTENCE_GEN2[]
|
||||||
import re
|
import re
|
||||||
import reprlib
|
import reprlib
|
||||||
|
|
||||||
RE_WORD = re.compile(r'\w+')
|
RE_WORD = re.compile('r\w+')
|
||||||
|
|
||||||
|
|
||||||
class Sentence:
|
class Sentence:
|
||||||
@@ -19,3 +20,5 @@ class Sentence:
|
|||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
for match in RE_WORD.finditer(self.text): # <2>
|
for match in RE_WORD.finditer(self.text): # <2>
|
||||||
yield match.group() # <3>
|
yield match.group() # <3>
|
||||||
|
|
||||||
|
# end::SENTENCE_GEN2[]
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Sentence: iterate over words using a generator expression
|
Sentence: iterate over words using a generator expression
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# BEGIN SENTENCE_GENEXP
|
# tag::SENTENCE_GENEXP[]
|
||||||
import re
|
import re
|
||||||
import reprlib
|
import reprlib
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ class Sentence:
|
|||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return (match.group() for match in RE_WORD.finditer(self.text))
|
return (match.group() for match in RE_WORD.finditer(self.text))
|
||||||
# END SENTENCE_GENEXP
|
# end::SENTENCE_GENEXP[]
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ WARNING: the Iterator Pattern is much simpler in idiomatic Python;
|
|||||||
see: sentence_gen*.py.
|
see: sentence_gen*.py.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# BEGIN SENTENCE_ITER
|
# tag::SENTENCE_ITER[]
|
||||||
import re
|
import re
|
||||||
import reprlib
|
import reprlib
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ class SentenceIterator:
|
|||||||
|
|
||||||
def __iter__(self): # <9>
|
def __iter__(self): # <9>
|
||||||
return self
|
return self
|
||||||
# END SENTENCE_ITER
|
# end::SENTENCE_ITER[]
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
import sys
|
import sys
|
||||||
|
|||||||
7
17-it-generator/tree/4steps/tree_step0.py
Normal file
7
17-it-generator/tree/4steps/tree_step0.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
def tree(cls):
|
||||||
|
yield cls.__name__
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
for cls_name in tree(BaseException):
|
||||||
|
print(cls_name)
|
||||||
10
17-it-generator/tree/4steps/tree_step1.py
Normal file
10
17-it-generator/tree/4steps/tree_step1.py
Normal 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}')
|
||||||
12
17-it-generator/tree/4steps/tree_step2.py
Normal file
12
17-it-generator/tree/4steps/tree_step2.py
Normal 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}')
|
||||||
10
17-it-generator/tree/4steps/tree_step3.py
Normal file
10
17-it-generator/tree/4steps/tree_step3.py
Normal 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}')
|
||||||
31
17-it-generator/tree/extra/pretty_tree.py
Normal file
31
17-it-generator/tree/extra/pretty_tree.py
Normal 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)
|
||||||
105
17-it-generator/tree/extra/test_pretty_tree.py
Normal file
105
17-it-generator/tree/extra/test_pretty_tree.py
Normal 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
|
||||||
94
17-it-generator/tree/extra/test_tree.py
Normal file
94
17-it-generator/tree/extra/test_tree.py
Normal 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
|
||||||
13
17-it-generator/tree/extra/tree.py
Normal file
13
17-it-generator/tree/extra/tree.py
Normal 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}')
|
||||||
8
17-it-generator/tree/step0/test_tree.py
Normal file
8
17-it-generator/tree/step0/test_tree.py
Normal 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
|
||||||
7
17-it-generator/tree/step0/tree.py
Normal file
7
17-it-generator/tree/step0/tree.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
def tree(cls):
|
||||||
|
yield cls.__name__
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
for cls_name in tree(BaseException):
|
||||||
|
print(cls_name)
|
||||||
25
17-it-generator/tree/step1/test_tree.py
Normal file
25
17-it-generator/tree/step1/test_tree.py
Normal 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
|
||||||
10
17-it-generator/tree/step1/tree.py
Normal file
10
17-it-generator/tree/step1/tree.py
Normal 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}')
|
||||||
25
17-it-generator/tree/step2/test_tree.py
Normal file
25
17-it-generator/tree/step2/test_tree.py
Normal 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
|
||||||
14
17-it-generator/tree/step2/tree.py
Normal file
14
17-it-generator/tree/step2/tree.py
Normal 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}')
|
||||||
38
17-it-generator/tree/step3/test_tree.py
Normal file
38
17-it-generator/tree/step3/test_tree.py
Normal 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
|
||||||
16
17-it-generator/tree/step3/tree.py
Normal file
16
17-it-generator/tree/step3/tree.py
Normal 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}')
|
||||||
76
17-it-generator/tree/step4/test_tree.py
Normal file
76
17-it-generator/tree/step4/test_tree.py
Normal 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
|
||||||
20
17-it-generator/tree/step4/tree.py
Normal file
20
17-it-generator/tree/step4/tree.py
Normal 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}')
|
||||||
94
17-it-generator/tree/step5/test_tree.py
Normal file
94
17-it-generator/tree/step5/test_tree.py
Normal 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
|
||||||
15
17-it-generator/tree/step5/tree.py
Normal file
15
17-it-generator/tree/step5/tree.py
Normal 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}')
|
||||||
94
17-it-generator/tree/step6/test_tree.py
Normal file
94
17-it-generator/tree/step6/test_tree.py
Normal 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
|
||||||
10
17-it-generator/tree/step6/tree.py
Normal file
10
17-it-generator/tree/step6/tree.py
Normal 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}')
|
||||||
@@ -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
|
DOI=10.1145/2544173.2509536 http://doi.acm.org/10.1145/2544173.2509536
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# BEGIN YIELD_DELEGATE_FAIL
|
# tag::YIELD_DELEGATE_FAIL[]
|
||||||
def f():
|
def f():
|
||||||
def do_yield(n):
|
def do_yield(n):
|
||||||
yield n
|
yield n
|
||||||
@@ -22,7 +22,7 @@ def f():
|
|||||||
while True:
|
while True:
|
||||||
x += 1
|
x += 1
|
||||||
do_yield(x)
|
do_yield(x)
|
||||||
# END YIELD_DELEGATE_FAIL
|
# end::YIELD_DELEGATE_FAIL[]
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
print('Invoking f() results in an infinite loop')
|
print('Invoking f() results in an infinite loop')
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ yielding.
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# BEGIN YIELD_DELEGATE_FIX
|
# tag::YIELD_DELEGATE_FIX[]
|
||||||
def f():
|
def f():
|
||||||
def do_yield(n):
|
def do_yield(n):
|
||||||
yield n
|
yield n
|
||||||
@@ -13,7 +13,7 @@ def f():
|
|||||||
while True:
|
while True:
|
||||||
x += 1
|
x += 1
|
||||||
yield from do_yield(x)
|
yield from do_yield(x)
|
||||||
# END YIELD_DELEGATE_FIX
|
# end::YIELD_DELEGATE_FIX[]
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
print('Invoking f() now produces a generator')
|
print('Invoking f() now produces a generator')
|
||||||
|
|||||||
Reference in New Issue
Block a user