blob: f3cba5677208ddd9fcd64dd2a7daef7376ebf981 [file] [log] [blame]
Tim Peters6ba5f792001-06-23 20:45:43 +00001tutorial_tests = """
Tim Peters1def3512001-06-23 20:27:04 +00002Let's try a simple generator:
3
4 >>> def f():
5 ... yield 1
6 ... yield 2
7
Tim Petersb9e9ff12001-06-24 03:44:52 +00008 >>> for i in f():
9 ... print i
10 1
11 2
Tim Peters1def3512001-06-23 20:27:04 +000012 >>> g = f()
13 >>> g.next()
14 1
15 >>> g.next()
16 2
17 >>> g.next()
18 Traceback (most recent call last):
19 File "<stdin>", line 1, in ?
20 File "<stdin>", line 2, in g
21 StopIteration
22
23"return" stops the generator:
24
25 >>> def f():
26 ... yield 1
27 ... return
28 ... yield 2 # never reached
29 ...
30 >>> g = f()
31 >>> g.next()
32 1
33 >>> g.next()
34 Traceback (most recent call last):
35 File "<stdin>", line 1, in ?
36 File "<stdin>", line 3, in f
37 StopIteration
38 >>> g.next() # once stopped, can't be resumed
39 Traceback (most recent call last):
40 File "<stdin>", line 1, in ?
41 StopIteration
42
43"raise StopIteration" stops the generator too:
44
45 >>> def f():
46 ... yield 1
47 ... return
48 ... yield 2 # never reached
49 ...
50 >>> g = f()
51 >>> g.next()
52 1
53 >>> g.next()
54 Traceback (most recent call last):
55 File "<stdin>", line 1, in ?
56 StopIteration
57 >>> g.next()
58 Traceback (most recent call last):
59 File "<stdin>", line 1, in ?
60 StopIteration
61
62However, they are not exactly equivalent:
63
64 >>> def g1():
65 ... try:
66 ... return
67 ... except:
68 ... yield 1
69 ...
70 >>> list(g1())
71 []
72
73 >>> def g2():
74 ... try:
75 ... raise StopIteration
76 ... except:
77 ... yield 42
78 >>> print list(g2())
79 [42]
80
81This may be surprising at first:
82
83 >>> def g3():
84 ... try:
85 ... return
86 ... finally:
87 ... yield 1
88 ...
89 >>> list(g3())
90 [1]
91
92Let's create an alternate range() function implemented as a generator:
93
94 >>> def yrange(n):
95 ... for i in range(n):
96 ... yield i
97 ...
98 >>> list(yrange(5))
99 [0, 1, 2, 3, 4]
100
101Generators always return to the most recent caller:
102
103 >>> def creator():
104 ... r = yrange(5)
105 ... print "creator", r.next()
106 ... return r
107 ...
108 >>> def caller():
109 ... r = creator()
110 ... for i in r:
111 ... print "caller", i
112 ...
113 >>> caller()
114 creator 0
115 caller 1
116 caller 2
117 caller 3
118 caller 4
119
120Generators can call other generators:
121
122 >>> def zrange(n):
123 ... for i in yrange(n):
124 ... yield i
125 ...
126 >>> list(zrange(5))
127 [0, 1, 2, 3, 4]
128
129"""
130
Tim Peters6ba5f792001-06-23 20:45:43 +0000131# The examples from PEP 255.
132
133pep_tests = """
134
135Specification: Return
136
137 Note that return isn't always equivalent to raising StopIteration: the
138 difference lies in how enclosing try/except constructs are treated.
139 For example,
140
141 >>> def f1():
142 ... try:
143 ... return
144 ... except:
145 ... yield 1
146 >>> print list(f1())
147 []
148
149 because, as in any function, return simply exits, but
150
151 >>> def f2():
152 ... try:
153 ... raise StopIteration
154 ... except:
155 ... yield 42
156 >>> print list(f2())
157 [42]
158
159 because StopIteration is captured by a bare "except", as is any
160 exception.
161
162Specification: Generators and Exception Propagation
163
164 >>> def f():
165 ... return 1/0
166 >>> def g():
167 ... yield f() # the zero division exception propagates
168 ... yield 42 # and we'll never get here
169 >>> k = g()
170 >>> k.next()
171 Traceback (most recent call last):
172 File "<stdin>", line 1, in ?
173 File "<stdin>", line 2, in g
174 File "<stdin>", line 2, in f
175 ZeroDivisionError: integer division or modulo by zero
176 >>> k.next() # and the generator cannot be resumed
177 Traceback (most recent call last):
178 File "<stdin>", line 1, in ?
179 StopIteration
180 >>>
181
182Specification: Try/Except/Finally
183
184 >>> def f():
185 ... try:
186 ... yield 1
187 ... try:
188 ... yield 2
189 ... 1/0
190 ... yield 3 # never get here
191 ... except ZeroDivisionError:
192 ... yield 4
193 ... yield 5
194 ... raise
195 ... except:
196 ... yield 6
197 ... yield 7 # the "raise" above stops this
198 ... except:
199 ... yield 8
200 ... yield 9
201 ... try:
202 ... x = 12
203 ... finally:
204 ... yield 10
205 ... yield 11
206 >>> print list(f())
207 [1, 2, 4, 5, 8, 9, 10, 11]
208 >>>
209
Tim Peters6ba5f792001-06-23 20:45:43 +0000210Guido's binary tree example.
211
212 >>> # A binary tree class.
213 >>> class Tree:
214 ...
215 ... def __init__(self, label, left=None, right=None):
216 ... self.label = label
217 ... self.left = left
218 ... self.right = right
219 ...
220 ... def __repr__(self, level=0, indent=" "):
221 ... s = level*indent + `self.label`
222 ... if self.left:
223 ... s = s + "\\n" + self.left.__repr__(level+1, indent)
224 ... if self.right:
225 ... s = s + "\\n" + self.right.__repr__(level+1, indent)
226 ... return s
227 ...
228 ... def __iter__(self):
229 ... return inorder(self)
230
231 >>> # Create a Tree from a list.
232 >>> def tree(list):
233 ... n = len(list)
234 ... if n == 0:
235 ... return []
236 ... i = n / 2
237 ... return Tree(list[i], tree(list[:i]), tree(list[i+1:]))
238
239 >>> # Show it off: create a tree.
240 >>> t = tree("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
241
242 >>> # A recursive generator that generates Tree leaves in in-order.
243 >>> def inorder(t):
244 ... if t:
245 ... for x in inorder(t.left):
246 ... yield x
247 ... yield t.label
248 ... for x in inorder(t.right):
249 ... yield x
250
251 >>> # Show it off: create a tree.
252 ... t = tree("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
253 ... # Print the nodes of the tree in in-order.
254 ... for x in t:
255 ... print x,
256 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
257
258 >>> # A non-recursive generator.
259 >>> def inorder(node):
260 ... stack = []
261 ... while node:
262 ... while node.left:
263 ... stack.append(node)
264 ... node = node.left
265 ... yield node.label
266 ... while not node.right:
267 ... try:
268 ... node = stack.pop()
269 ... except IndexError:
270 ... return
271 ... yield node.label
272 ... node = node.right
273
274 >>> # Exercise the non-recursive generator.
275 >>> for x in t:
276 ... print x,
277 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
278
279"""
280
281# A few examples from Iterator-List and Python-Dev email.
282
283email_tests = """
284
285The difference between yielding None and returning it.
286
287>>> def g():
288... for i in range(3):
289... yield None
290... yield None
291... return
292>>> list(g())
293[None, None, None, None]
294
295Ensure that explicitly raising StopIteration acts like any other exception
296in try/except, not like a return.
297
298>>> def g():
299... yield 1
300... try:
301... raise StopIteration
302... except:
303... yield 2
304... yield 3
305>>> list(g())
306[1, 2, 3]
Tim Petersb9e9ff12001-06-24 03:44:52 +0000307
308A generator can't be resumed while it's already running.
309
310>>> def g():
311... i = me.next()
312... yield i
313>>> me = g()
314>>> me.next()
315Traceback (most recent call last):
316 ...
317 File "<string>", line 2, in g
318ValueError: generator already executing
Tim Peters6ba5f792001-06-23 20:45:43 +0000319"""
320
Tim Peters0f9da0a2001-06-23 21:01:47 +0000321# Fun tests (for sufficiently warped notions of "fun").
322
323fun_tests = """
324
325Build up to a recursive Sieve of Eratosthenes generator.
326
327>>> def firstn(g, n):
328... return [g.next() for i in range(n)]
329
330>>> def intsfrom(i):
331... while 1:
332... yield i
333... i += 1
334
335>>> firstn(intsfrom(5), 7)
336[5, 6, 7, 8, 9, 10, 11]
337
338>>> def exclude_multiples(n, ints):
339... for i in ints:
340... if i % n:
341... yield i
342
343>>> firstn(exclude_multiples(3, intsfrom(1)), 6)
344[1, 2, 4, 5, 7, 8]
345
346>>> def sieve(ints):
347... prime = ints.next()
348... yield prime
349... not_divisible_by_prime = exclude_multiples(prime, ints)
350... for p in sieve(not_divisible_by_prime):
351... yield p
352
353>>> primes = sieve(intsfrom(2))
354>>> firstn(primes, 20)
355[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71]
Tim Petersb9e9ff12001-06-24 03:44:52 +0000356
357Another famous problem: generate all integers of the form
358 2**i * 3**j * 5**k
359in increasing order, where i,j,k >= 0. Trickier than it may look at first!
360Try writing it without generators, and correctly, and without generating
3613 internal results for each result output.
362
363>>> def times(n, g):
364... for i in g:
365... yield n * i
366>>> firstn(times(10, intsfrom(1)), 10)
367[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
368
369>>> def merge(g, h):
370... ng = g.next()
371... nh = h.next()
372... while 1:
373... if ng < nh:
374... yield ng
375... ng = g.next()
376... elif ng > nh:
377... yield nh
378... nh = h.next()
379... else:
380... yield ng
381... ng = g.next()
382... nh = h.next()
383
384This works, but is doing a whale of a lot or redundant work -- it's not
385clear how to get the internal uses of m235 to share a single generator.
386Note that me_times2 (etc) each need to see every element in the result
387sequence. So this is an example where lazy lists are more natural (you
388can look at the head of a lazy list any number of times).
389
390>>> def m235():
391... yield 1
392... me_times2 = times(2, m235())
393... me_times3 = times(3, m235())
394... me_times5 = times(5, m235())
395... for i in merge(merge(me_times2,
396... me_times3),
397... me_times5):
398... yield i
399
400>>> result = m235()
401>>> for i in range(5):
402... print firstn(result, 15)
403[1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24]
404[25, 27, 30, 32, 36, 40, 45, 48, 50, 54, 60, 64, 72, 75, 80]
405[81, 90, 96, 100, 108, 120, 125, 128, 135, 144, 150, 160, 162, 180, 192]
406[200, 216, 225, 240, 243, 250, 256, 270, 288, 300, 320, 324, 360, 375, 384]
407[400, 405, 432, 450, 480, 486, 500, 512, 540, 576, 600, 625, 640, 648, 675]
Tim Peters0f9da0a2001-06-23 21:01:47 +0000408"""
409
Tim Peters6ba5f792001-06-23 20:45:43 +0000410__test__ = {"tut": tutorial_tests,
411 "pep": pep_tests,
Tim Peters0f9da0a2001-06-23 21:01:47 +0000412 "email": email_tests,
413 "fun": fun_tests}
Tim Peters1def3512001-06-23 20:27:04 +0000414
415# Magic test name that regrtest.py invokes *after* importing this module.
416# This worms around a bootstrap problem.
417# Note that doctest and regrtest both look in sys.argv for a "-v" argument,
418# so this works as expected in both ways of running regrtest.
419def test_main():
420 import doctest, test_generators
421 doctest.testmod(test_generators)
422
423# This part isn't needed for regrtest, but for running the test directly.
424if __name__ == "__main__":
425 test_main()