在Python中最佳使用yield的地方?
我知道yield
是怎么工作的。我也知道排列组合,可以把它看作是数学上的简单概念。
但是yield
的真正作用是什么呢?我什么时候应该使用它呢?如果能给个简单好懂的例子就更好了。
4 个回答
另一个用法是在网络客户端中。使用“yield”这个关键字在生成器函数里,可以在多个套接字之间轮流处理,而不需要复杂的线程。
举个例子,我有一个硬件测试客户端,需要把一张图片的红、绿、蓝三个颜色通道的数据发送给固件。这些数据需要按照顺序发送:红色、绿色、蓝色,红色、绿色、蓝色。与其创建三个线程,我用一个生成器来从文件中读取数据,并编码成缓冲区。每个缓冲区都是通过“yield buf”来返回的。当文件读取完毕,函数就结束了,这样我就知道迭代结束了。
我的客户端代码循环调用这三个生成器函数,获取缓冲区,直到迭代结束。
简单来说,yield
可以让你得到一个生成器。你可以在函数中用它代替通常的 return
。下面是一个非常简单的例子,直接从提示中复制过来的……
>>> def get_odd_numbers(i):
... return range(1, i, 2)
...
>>> def yield_odd_numbers(i):
... for x in range(1, i, 2):
... yield x
...
>>> foo = get_odd_numbers(10)
>>> bar = yield_odd_numbers(10)
>>> foo
[1, 3, 5, 7, 9]
>>> bar
<generator object yield_odd_numbers at 0x1029c6f50>
>>> next(bar)
1
>>> next(bar)
3
>>> next(bar)
5
从上面的例子可以看到,在第一个情况下,foo
会把整个列表一次性放在内存里。对于一个只有5个元素的列表来说,这没什么大不了的,但如果你想要一个有500万个元素的列表呢?这不仅会占用大量内存,而且在调用函数时也会花费很多时间来构建这个列表。在第二个情况下,bar
只是给你一个生成器。生成器是一种可迭代的对象——这意味着你可以在 for 循环中使用它等等,但每个值只能访问一次。而且所有的值并不是同时存储在内存里的;生成器对象会“记住”上一次你调用它时的循环位置——这样,如果你想用可迭代对象来(比如说)数到500亿,你就不需要一次性数到500亿并把这500亿个数字都存起来。再次强调,这只是一个很简单的例子,如果你真的想数到500亿,可能会用到 itertools
。:)
这就是生成器最简单的用法。正如你所说,它可以用来写高效的排列组合,使用 yield
将数据推送到调用栈中,而不是使用某种堆栈变量。生成器还可以用于特殊的树遍历,以及其他各种用途。
进一步阅读:
- python wiki http://wiki.python.org/moin/Generators
- 关于生成器的PEP http://www.python.org/dev/peps/pep-0255/
yield
最适合用在你有一个函数需要返回一系列数据,而你又不想一次性把所有数据都放在内存里的时候。
举个例子,我有一个 Python 脚本,它需要处理很多 CSV 文件中的数据,我想把每一行数据传给另一个函数去处理。我不想一次性把几兆的数据都存到内存里,所以我用 yield
每次返回一行数据。获取文件中每一行的函数可能看起来像这样:
def get_lines(files):
for f in files:
for line in f:
#preprocess line
yield line
然后我可以用和列表一样的方式来访问这个函数的输出:
for line in get_lines(files):
#process line
这样我就节省了很多内存的使用。