Substantial rewriting.

This commit is contained in:
Tim Peters 2001-06-14 06:57:29 +00:00
parent b54a36962d
commit 00522cd5dc
1 changed files with 85 additions and 56 deletions

View File

@ -91,7 +91,8 @@ Motivation
that the function can be resumed again right where it left off. A
very simple example:
def fib(): a, b = 0, 1
def fib():
a, b = 0, 1
while 1:
yield b
a, b = b, a+b
@ -118,92 +119,119 @@ Motivation
Specification
A new statement, the "yield" statement, is introduced:
A new statement is introduced:
yield_stmt: "yield" [expression_list]
yield_stmt: "yield" expression_list
This statement may only be used inside functions. A function which
contains a yield statement is a so-called "generator function". A
generator function may not contain return statements of the form:
"yield" is a new keyword, so a future statement[8] is needed to phase
this in. [XXX spell this out]
"return" expression_list
The yield statement may only be used inside functions. A function that
contains a yield statement is called a generator function.
It may, however, contain return statements of the form:
When a generator function is called, the actual arguments are bound to
function-local formal argument names in the usual way, but no code in
the body of the function is executed. Instead a generator-iterator
object is returned; this conforms to the iterator protocol[6], so in
particular can be used in for-loops in a natural way. Note that when
the intent is clear from context, the unqualified name "generator" may
be used to refer either to a generator-function or a generator-
iterator.
Each time the .next() method of a generator-iterator is invoked, the
code in the body of the generator-function is executed until a yield
or return statement (see below) is encountered, or until the end of
the body is reached.
If a yield statement is encountered, the state of the function is
frozen, and the value of expression_list is returned to .next()'s
caller. By "frozen" we mean that all local state is retained,
including the current bindings of local variables, the instruction
pointer, and the internal evaluation stack: enough information is
saved so that the next time .next() is invoked, the function can
proceed exactly is if the yield statement were just another external
call.
A generator function can also contain return statements of the form:
"return"
When a generator function is called, an iterator[6] is returned.
Each time the .next() method of this iterator is called, the code
in the body of the generator function is executed until a yield
statement or a return statement is encountered, or until the end
of the body is reached.
Note that an expression_list is not allowed on return statements
in the body of a generator (although, of course, they may appear in
the bodies of non-generator functions nested within the generator).
If a yield statement is encountered during this execution, the
state of the function is frozen, and a value is returned to the
object calling .next(). If an empty yield statement was
encountered, None is returned; otherwise, the given expression(s)
is (are) returned.
When a return statement is encountered, nothing is returned, but a
StopIteration exception is raised, signalling that the iterator is
exhausted. The same is true if control flows off the end of the
function. Note that return means "I'm done, and have nothing
interesting to return", for both generator functions and non-generator
functions.
If an empty return statement is encountered, nothing is returned;
however, a StopIteration exception is raised, signalling that the
iterator is exhausted.
An example of how generators may be used is given below:
Example
# A binary tree class
# A binary tree class.
class Tree:
def __init__(self, label, left=None, right=None):
self.label = label
self.left = left
self.right = right
def __repr__(self, level=0, indent=" "):
def __repr__(self, level=0, indent=" "):
s = level*indent + `self.label`
if self.left:
s = s + "\n" + self.left.__repr__(level+1, indent)
if self.right:
s = s + "\n" + self.right.__repr__(level+1, indent)
return s
def __iter__(self):
return inorder(self)
# A function that creates a tree from a list
# Create a Tree from a list.
def tree(list):
if not len(list):
n = len(list)
if n == 0:
return []
i = len(list)/2
i = n / 2
return Tree(list[i], tree(list[:i]), tree(list[i+1:]))
# A recursive generator that generates the tree leaves in in-order
# A recursive generator that generates Tree leaves in in-order.
def inorder(t):
if t:
for x in inorder(t.left): yield x
for x in inorder(t.left):
yield x
yield t.label
for x in inorder(t.right): yield x
# Show it off: create a tree
for x in inorder(t.right):
yield x
# Show it off: create a tree.
t = tree("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
# Print the nodes of the tree in in-order
for x in t: print x,
# Print the nodes of the tree in in-order.
for x in t:
print x,
print
# A non-recursive generator.
def inorder(node):
stack = []
while node:
while node.left:
stack.append(node)
node = node.left
yield node.label
while not node.right:
try:
node = stack.pop()
except IndexError:
return
yield node.label
node = node.right
# Exercise the non-recursive generator
for x in t: print x,
stack = []
while node:
while node.left:
stack.append(node)
node = node.left
yield node.label
while not node.right:
try:
node = stack.pop()
except IndexError:
return
yield node.label
node = node.right
# Exercise the non-recursive generator.
for x in t:
print x,
print
@ -214,16 +242,17 @@ Reference Implementation
Footnotes and References
[1] PEP 234, http://python.sourceforge.net/peps/pep-0234.html
[1] PEP 234, http://python.sf.net/peps/pep-0234.html
[2] http://www.stackless.com/
[3] PEP 219, http://python.sourceforge.net/peps/pep-0219.html
[3] PEP 219, http://python.sf.net/peps/pep-0219.html
[4] "Iteration Abstraction in Sather"
Murer , Omohundro, Stoutamire and Szyperski
http://www.icsi.berkeley.edu/~sather/Publications/toplas.html
[5] http://www.cs.arizona.edu/icon/
[6] The concept of iterators is described in PEP 234
http://python.sourceforge.net/peps/pep-0234.html
http://python.sf.net/peps/pep-0234.html
[7] http://python.ca/nas/python/generator.diff
[8] http://python.sf.net/peps/pep-0236.html
Copyright