您å�¯èƒ½å�¬è¯´è¿‡ï¼Œå¸¦æœ‰ yield 的函数在 Python ä¸è¢«ç§°ä¹‹ä¸º generator(生æˆ�器),何谓 generator ?
我们先抛开 generator,以一个常�的编程题目�展示 yield 的概念。
如何生��波那契數列
æ–�波那契(Fibonacci)數列是一个é�žå¸¸ç®€å�•的递归数列,除第一个和第二个数外,任æ„�一个数都å�¯ç”±å‰�ä¸¤ä¸ªæ•°ç›¸åŠ å¾—åˆ°ã€‚ç”¨è®¡ç®—æœºç¨‹åº�输出æ–�波那契數列的å‰� N 个数是一个é�žå¸¸ç®€å�•的问题,许多åˆ�å¦è€…都å�¯ä»¥è½»æ˜“写出如下函数:
清� 1. 简�输出�波那契數列� N 个数
实例
执行以上代ç �,我们å�¯ä»¥å¾—到如下输出:
1 1 2 3 5
结果没有问题,但有ç»�验的开å�‘者会指出,直接在 fab 函数ä¸ç”¨ print 打å�°æ•°å—会导致该函数å�¯å¤�ç”¨æ€§è¾ƒå·®ï¼Œå› ä¸º fab 函数返回 Noneï¼Œå…¶ä»–å‡½æ•°æ— æ³•èŽ·å¾—è¯¥å‡½æ•°ç”Ÿæˆ�的数列。
��高 fab 函数的��用性,最好��直接打�出数列,而是返回一个 List。以下是 fab 函数改写�的第二个版本:
清� 2. 输出�波那契數列� N 个数第二版
实例
�以使用如下方�打�出 fab 函数返回的 List:
1 1 2 3 5
改写å�Žçš„ fab 函数通过返回 List 能满足å¤�用性的è¦�求,但是更有ç»�验的开å�‘者会指出,该函数在è¿�行ä¸å� 用的内å˜ä¼šéš�ç�€å�‚æ•° max 的增大而增大,如果è¦�控制内å˜å� 用,最好ä¸�è¦�用 List
æ�¥ä¿�å˜ä¸é—´ç»“果,而是通过 iterable 对象æ�¥è¿ä»£ã€‚例如,在 Python2.x ä¸ï¼Œä»£ç �:
清å�• 3. 通过 iterable 对象æ�¥è¿ä»£
for i in range(1000): pass
会导致生æˆ�一个 1000 ä¸ªå…ƒç´ çš„ List,而代ç �:
for i in xrange(1000): pass
则ä¸�会生æˆ�一个 1000 ä¸ªå…ƒç´ çš„ List,而是在æ¯�次è¿ä»£ä¸è¿”回下一个数值,内å˜ç©ºé—´å� 用很å°�ã€‚å› ä¸º xrange ä¸�返回 List,而是返回一个 iterable 对象。
利用 iterable 我们�以把 fab 函数改写为一个支� iterable 的 class,以下是第三个版本的 Fab:
清� 4. 第三个版本
实例
Fab 类通过 next() ä¸�æ–返回数列的下一个数,内å˜å� 用始终为常数:
1 1 2 3 5
然而,使用 class 改写的这个版本,代ç �远远没有第一版的 fab 函数æ�¥å¾—简æ´�。如果我们想è¦�ä¿�æŒ�第一版 fab 函数的简æ´�性,å�Œæ—¶å�ˆè¦�获得 iterable 的效果,yield 就派上用场了:
清� 5. 使用 yield 的第四版
实例
第四个版本的 fab 和第一版相比,仅仅把 print b 改为了 yield b,就在��简�性的�时获得了 iterable 的效果。
调用第四版的 fab 和第二版的 fab 完全一致:
1 1 2 3 5
简å�•地讲,yield 的作用就是把一个函数å�˜æˆ�一个 generator,带有 yield 的函数ä¸�å†�是一个普通函数,Python 解释器会将其视为一个 generator,调用 fab(5) ä¸�会执行 fab 函数,而是返回一个 iterable 对象ï¼�在 for 循环执行时,æ¯�次循环都会执行 fab 函数内部的代ç �,执行到 yield b 时,fab 函数就返回一个è¿ä»£å€¼ï¼Œä¸‹æ¬¡è¿ä»£æ—¶ï¼Œä»£ç �从 yield b 的下一æ�¡è¯å�¥ç»§ç»æ‰§è¡Œï¼Œè€Œå‡½æ•°çš„æœ¬åœ°å�˜é‡�看起æ�¥å’Œä¸Šæ¬¡ä¸æ–执行å‰�æ˜¯å®Œå…¨ä¸€æ ·çš„ï¼ŒäºŽæ˜¯å‡½æ•°ç»§ç»æ‰§è¡Œï¼Œç›´åˆ°å†�次é�‡åˆ° yield。
也å�¯ä»¥æ‰‹åŠ¨è°ƒç”¨ fab(5) çš„ next() æ–¹æ³•ï¼ˆå› ä¸º fab(5) 是一个 generator 对象,该对象具有 next() æ–¹æ³•ï¼‰ï¼Œè¿™æ ·æˆ‘ä»¬å°±å�¯ä»¥æ›´æ¸…楚地看到 fab 的执行æµ�程:
清� 6. 执行�程
当函数执行结æ�Ÿæ—¶ï¼Œgenerator 自动抛出 StopIteration 异常,表示è¿ä»£å®Œæˆ�。在 for å¾ªçŽ¯é‡Œï¼Œæ— éœ€å¤„ç�† StopIteration 异常,循环会æ£å¸¸ç»“æ�Ÿã€‚
我们�以得出以下结论:
一个带有 yield 的函数就是一个 generator,它和普通函数ä¸�å�Œï¼Œç”Ÿæˆ�一个 generator 看起æ�¥åƒ�函数调用,但ä¸�会执行任何函数代ç �,直到对其调用 next()(在 for 循环ä¸ä¼šè‡ªåŠ¨è°ƒç”¨ next())æ‰�开始执行。虽然执行æµ�程ä»�按函数的æµ�程执行,但æ¯�执行到一个 yield è¯å�¥å°±ä¼šä¸æ–,并返回一个è¿ä»£å€¼ï¼Œä¸‹æ¬¡æ‰§è¡Œæ—¶ä»Ž yield 的下一个è¯å�¥ç»§ç»æ‰§è¡Œã€‚看起æ�¥å°±å¥½åƒ�一个函数在æ£å¸¸æ‰§è¡Œçš„过程ä¸è¢« yield 䏿–了数次,æ¯�æ¬¡ä¸æ–都会通过 yield 返回当å‰�çš„è¿ä»£å€¼ã€‚
yield 的好处是显而易è§�的,把一个函数改写为一个 generator 就获得了è¿ä»£èƒ½åŠ›ï¼Œæ¯”èµ·ç”¨ç±»çš„å®žä¾‹ä¿�å˜çжæ€�æ�¥è®¡ç®—下一个 next() 的值,ä¸�仅代ç �简æ´�,而且执行æµ�程异常清晰。
如何判æ–一个函数是å�¦æ˜¯ä¸€ä¸ªç‰¹æ®Šçš„ generator 函数?å�¯ä»¥åˆ©ç”¨ isgeneratorfunction 判æ–:
清å�• 7. 使用 isgeneratorfunction 判æ–
�注�区分 fab 和 fab(5),fab 是一个 generator function,而 fab(5) 是调用 fab 返回的一个 generator,好比类的定义和类的实例的区别:
清� 8. 类的定义和类的实例
�次调用 fab 函数都会生�一个新的 generator 实例,�实例互�影�:
return 的作用
在一个 generator function ä¸ï¼Œå¦‚果没有 returnï¼Œåˆ™é»˜è®¤æ‰§è¡Œè‡³å‡½æ•°å®Œæ¯•ï¼Œå¦‚æžœåœ¨æ‰§è¡Œè¿‡ç¨‹ä¸ return,则直接抛出 StopIteration 终æ¢è¿ä»£ã€‚
å�¦ä¸€ä¸ªä¾‹å�
å�¦ä¸€ä¸ª yield 的例å�æ�¥æº�于文件读å�–。如果直接对文件对象调用 read() 方法,会导致ä¸�å�¯é¢„测的内å˜å� 用。好的方法是利用固定长度的缓冲区æ�¥ä¸�æ–读å�–文件内容。通过 yield,我们ä¸�å†�需è¦�编写读文件的è¿ä»£ç±»ï¼Œå°±å�¯ä»¥è½»æ�¾å®žçŽ°æ–‡ä»¶è¯»å�–:
清å�• 9. å�¦ä¸€ä¸ª yield 的例å�
实例
以上仅仅简å�•介ç»�了 yield 的基本概念和用法,yield 在 Python 3 ä¸è¿˜æœ‰æ›´å¼ºå¤§çš„用法,我们会在å�Žç»æ–‡ç« ä¸è®¨è®ºã€‚
注:本文的代ç �å�‡åœ¨ Python 2.7 ä¸è°ƒè¯•通过
原文地�:https://www.ibm.com/developerworks/cn/opensource/os-cn-python-yield/