# [译] PEP 255--简单的生成器

2019.01.19 15:18 907浏览

## 动机

``````def fib():
a, b = 0, 1
while 1:
yield b
a, b = b, a+b
``````

## 设计规格：yield

yield_stmt：“yield”expression_list

yield 是一个新的关键字，因此需要一个 `future` 声明【注释8】来进行引入：在早期版本中，若想使用生成器的模块，必须在接近头部处包含以下行（详见 PEP 236）：

``````from __future__ import generators
``````

yield 语句只能在函数内部使用。包含 yield 语句的函数被称为生成器函数。从各方面来看，生成器函数都只是个普通函数，但在它的代码对象的 co_flags 中设置了新的“CO_GENERATOR”标志。

``````>>> def g():
...     i = me.next()
...     yield i
>>> me = g()
>>> me.next()
Traceback (most recent call last):
...
File "<string>", line 2, in g
``````

## 设计规格：return

``````return
``````

``````>>> def f1():
...     try:
...         return
...     except:
...        yield 1
>>> print list(f1())
[]
``````

``````>>> def f2():
...     try:
...         raise StopIteration
...     except:
...         yield 42
>>> print list(f2())
[42]
``````

## 设计规格：生成器和异常传播

``````>>> def f():
...     return 1/0
>>> def g():
...     yield f()  # the zero division exception propagates
...     yield 42   # and we'll never get here
>>> k = g()
>>> k.next()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 2, in g
File "<stdin>", line 2, in f
ZeroDivisionError: integer division or modulo by zero
>>> k.next()  # and the generator cannot be resumed
Traceback (most recent call last):
File "<stdin>", line 1, in ?
StopIteration
>>>
``````

## 设计规格：Try/Exception/Finally

``````>>> def f():
...     try:
...         yield 1
...         try:
...             yield 2
...             1/0
...             yield 3  # never get here
...         except ZeroDivisionError:
...             yield 4
...             yield 5
...             raise
...         except:
...             yield 6
...         yield 7     # the "raise" above stops this
...     except:
...         yield 8
...     yield 9
...     try:
...         x = 12
...     finally:
...        yield 10
...     yield 11
>>> print list(f())
[1, 2, 4, 5, 8, 9, 10, 11]
>>>
``````

## 示例

``````# 二叉树类
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="    "):
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)

# 从列表中创建 Tree
def tree(list):
n = len(list)
if n == 0:
return []
i = n / 2
return Tree(list[i], tree(list[:i]), tree(list[i+1:]))

# 递归生成器，按顺序生成树标签
def inorder(t):
if t:
for x in inorder(t.left):
yield x
yield t.label
for x in inorder(t.right):
yield x

# 展示：创建一棵树
t = tree("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
# 按顺序打印树的节点
for x in t:
print x,
print

# 非递归生成器
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

# 练习非递归生成器
for x in t:
print x,
print
Both output blocks display:

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
``````

## 问答

### 为什么用新的关键字yield而非内置函数？

Python 中通过关键字能更好地表达控制流，即 yield 是一个控制结构。而且为了 Jython 的高效实现，编译器需要在编译时就确定潜在的挂起点，新的关键字会使这一点变得简单。CPython 的实现也大量利用它来检测哪些函数是生成器函数（尽管一个新的关键字替代 def 就能解决 CPython 的问题，但人们问“为什么要新的关键字”问题时，并不想要新的关键字）。

### 为什么不是其它不带新关键字的特殊语法？

``````return 3 and continue
return and continue 3
return generating 3
continue return 3
return >> , 3
from generator return 3
return >> 3
return << 3
>> 3
<< 3
* 3
``````

### 为什么允许用return，而不强制用StopIteration？

“StopIteration”的机制是底层细节，就像 Python 2.1 中的“IndexError”的机制一样：实现时需要做一些预先定义好的东西，而 Python 为高级用户开放了这些机制。尽管不强制要求每个人都在这个层级工作。 “return”在任何一种函数中都意味着“我已经完成”，这很容易解读和使用。注意，`return` 并不总是等同于 try-except 结构中的 `raise StopIteration`（参见“设计规格：Return”部分）。

## BDFL声明

### BDFL

def 留了下来。任何一方都没有任何争论是完全令人信服的，所以我咨询了我的语言设计师的直觉。它告诉我 PEP 中提出的语法是完全正确的——不是太热，也不是太冷。但是，就像希腊神话中的 Delphi（译注：特尔斐，希腊古都） 的甲骨文一样，它并没有告诉我原因，所以我没有对反对此 PEP 语法的论点进行反驳。 我能想出的最好的（除了已经同意做出的反驳）是“FUD”（译注：缩写自 fear、uncertainty 和 doubt）。 如果这从第一天开始就是语言的一部分，我非常怀疑这早已让安德鲁·库奇林（Andrew Kuchling）的“Python Warts”页面成为可能。（译注：wart 是疣，一种难看的皮肤病。这是一个 wiki 页面，列举了对 Python 吹毛求疵的建议）。

## 脚注和参考文献

[1] PEP-234, Iterators, Yee, Van Rossum

[3] PEP-219, Stackless Python, McMillan

[4] “Iteration Abstraction in Sather” Murer, Omohundro, Stoutamire and Szyperski

[6] The concept of iterators is described in PEP 234. See [1] above.

[8] PEP 236, Back to the future, Peters

[9] To experiment with this implementation, check out Python from CVS according to the instructions at http://sf.net/cvs/?group_id=5470 ，Note that the std test Lib/test/test_generators.py contains many examples, including all those in this PEP.

## 版权信息

（译文完）

PS：官方 PEP 有将近500个，然而保守估计，被翻译成中文的不足20个（去重的情况下）。我好奇，感兴趣将一些重要的 PEP 翻译出来的人有多少呢？现抛此问题出来探探路，欢迎留言交流。

-----------------

1人点赞

• 1
• 评论
• 收藏
• 共同学习，写下你的评论

0/150