Substantial rewriting.
This commit is contained in:
parent
b54a36962d
commit
00522cd5dc
141
pep-0255.txt
141
pep-0255.txt
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue