Python 的generator

  通常我们会有一个函数get_x_data来提供某些数据,然后另外的一个函数handle_x_data遍历那些数据进行处理。这时就会出现一个问题, 如果get_x_data 返回大量数据,这些数据就会占用很多内存,同时程序又会停在get_x_data 上很长时间。那么能不能让get_x_data 找到一个值,就传递给handle_x_data 处理,处理完了,get_x_data 把下一个值交给handle_x_data 进行处理,就这样一个个的处理,既避免了内存的浪费,也避免了程序在get_x_data 上停留太多时间。例如:

for i in get_x_data():
  handle_x_data(i)

这里的get_x_data() 并没有立即返回所有data, 而是每次返回一个,待handld_x_data() 处理完这个data, 再返回下一个。

在Python 中很容易很优雅地做到!

 

Python 中有个range 函数,在Python 2 中,range() 会返回一个list, 会将这个list 生成好,放入内存。 而如果想使用逐个返回的方法,就需要使用xrange. Python 3 中已经没有xrange, 而range 则返回一个generator 而不再是一个list.

模拟xrange:

def xrange(n):
    i = 0
    while i < n:
        yield i
        i += 1

for it in xrange(10):
    print(it)        

xrange 每次执行完yield i, i 就会被传递到it 中, 而停止在下一次yield 处,直到for 语句向range 要下一个值。

实际中的应用:

elasticsearch 为了避免客户端一次请求太多数据而占用太多客户端内存的问题,它提供了另外一种途径,根据客户端的查询条件,为客户端生成一个scroll id。 然后客户端根据这个scoll id 多次小批量地请求所需要的data. 所以如果应用原生的elasticsearch api 就比较麻烦,需要先获得scroll id, 然后再根据scroll id 多次请求。

我们可以通过yield 很好的包装这种请求:

 

def get_xxxx(self, query):
"""
return a generator, you can query the data in the way of
for i in yyyy.get_xxx(query):
print(i)
"""
result = []
scroll_id = self.__scroll_id(query) // get scroll id
result = self.__get_scroll_data(scroll_id) // get data from scroll id, result is a list
while result: // result is not empty
    yield result.pop()
    if not result: // result is empty, fetch next batch of data
        result = self.__get_scroll_data(scroll_id)    

 

posted @ 2014-08-10 11:35  zhifan  阅读(215)  评论(0)    收藏  举报