黄冈网站建设报价表,app使用什么做的网站,摄影网站都有什么,推荐做素菜的网站目录
generator基础 generator应用 generator基础应用 generator高级应用 注意事项#xff1a;
正文 本文将由浅入深详细介绍yield以及generator#xff0c;包括以下内容#xff1a;什么generator#xff0c;生成generator的方法#xff0c;generator的特点#…目录
generator基础 generator应用 generator基础应用 generator高级应用 注意事项
正文 本文将由浅入深详细介绍yield以及generator包括以下内容什么generator生成generator的方法generator的特点generator基础及高级应用场景generator使用中的注意事项。本文不包括enhanced generator即pep342相关内容这部分内容在之后的博文介绍。
generator基础 回到顶部 在python的函数function定义中只要出现了yield表达式Yield expression那么事实上定义的是一个generator function 调用这个generator function返回值是一个generator。这根普通的函数调用有所区别For example
def gen_generator():yield 1def gen_value():return 1if __name__ __main__:ret gen_generator()print ret, type(ret) #generator object gen_generator at 0x02645648 type generatorret gen_value()print ret, type(ret) # 1 type int 从上面的代码可以看出gen_generator函数返回的是一个generator实例generator有以下特别
遵循迭代器iterator协议迭代器协议需要实现__iter__、next接口 能过多次进入、多次返回能够暂停函数体中代码的执行 下面看一下测试代码 def gen_example():... print before any yield... yield first yield... print between yields... yield second yield... print no yield anymore... gen gen_example() gen.next() 第一次调用nextbefore any yieldfirst yield gen.next() 第二次调用nextbetween yieldssecond yield gen.next() 第三次调用nextno yield anymoreTraceback (most recent call last):File stdin, line 1, in moduleStopIteratio 调用gen example方法并没有输出任何内容说明函数体的代码尚未开始执行。当调用generator的next方法generator会执行到yield 表达式处返回yield表达式的内容然后暂停挂起在这个地方所以第一次调用next打印第一句并返回“first yield”。 暂停意味着方法的局部变量指针信息运行环境都保存起来直到下一次调用next方法恢复。第二次调用next之后就暂停在最后一个yield再次调用next()方法则会抛出StopIteration异常。 因为for语句能自动捕获StopIteration异常所以generator本质上是任何iterator较为常用的方法是在循环中使用 1 def generator_example():
2 yield 1
3 yield 2
4
5 if __name__ __main__:
6 for e in generator_example():
7 print e
8 # output 1 2 generator function产生的generator与普通的function有什么区别呢 1function每次都是从第一行开始运行而generator从上一次yield开始的地方运行 2function调用一次返回一个一组值而generator可以多次返回 3function可以被无数次重复调用而一个generator实例在yield最后一个值 或者return之后就不能继续调用了 在函数中使用Yield然后调用该函数是生成generator的一种方式。另一种常见的方式是使用generator expressionFor example gen (x * x for x in xrange(5)) print gen generator object genexpr at 0x02655710
generator应用 回到顶部 generator基础应用 为什么使用generator呢最重要的原因是可以按需生成并“返回”结果而不是一次性产生所有的返回值况且有时候根本就不知道“所有的返回值”。比如对于下面的代码 1 RANGE_NUM 100
2 for i in [x*x for x in range(RANGE_NUM)]: # 第一种方法对列表进行迭代
3 # do sth for example
4 print i
5
6 for i in (x*x for x in range(RANGE_NUM)): # 第二种方法对generator进行迭代
7 # do sth for example
8 print i 在上面的代码中两个for语句输出是一样的代码字面上看来也就是中括号与小括号的区别。但这点区别差异是很大的第一种方法返回值是一个列表第二个方法返回的是一个generator对象。随着RANGE_NUM的变大第一种方法返回的列表也越大占用的内存也越大但是对于第二种方法没有任何区别。 我们再来看一个可以“返回”无穷多次的例子
def fib(): a, b 1, 1 while True: yield a a, b b, ab 这个generator拥有生成无数多“返回值”的能力使用者可以自己决定什么时候停止迭代 generator高级应用 使用场景一 Generator可用于产生数据流 generator并不立刻产生返回值而是等到被需要的时候才会产生返回值相当于一个主动拉取的过程(pull)比如现在有一个日志文件每行产生一条记录对于每一条记录不同部门的人可能处理方式不同但是我们可以提供一个公用的、按需生成的数据流。 1 def gen_data_from_file(file_name):2 for line in file(file_name):3 yield line4 5 def gen_words(line):6 for word in (w for w in line.split() if w.strip()):7 yield word8 9 def count_words(file_name):
10 word_map {}
11 for line in gen_data_from_file(file_name):
12 for word in gen_words(line):
13 if word not in word_map:
14 word_map[word] 0
15 word_map[word] 1
16 return word_map
17
18 def count_total_chars(file_name):
19 total 0
20 for line in gen_data_from_file(file_name):
21 total len(line)
22 return total
23
24 if __name__ __main__:
25 print count_words(test.txt), count_total_chars(test.txt) 上面的例子来自08年的PyCon一个讲座。gen_words gen_data_from_file是数据生产者而count_words count_total_chars是数据的消费者。可以看到数据只有在需要的时候去拉取的而不是提前准备好。另外gen_words中 (w for w in line.split() if w.strip()) 也是产生了一个generator 使用场景二 一些编程场景中一件事情可能需要执行一部分逻辑然后等待一段时间、或者等待某个异步的结果、或者等待某个状态然后继续执行另一部分逻辑。比如微服务架构中服务A执行了一段逻辑之后去服务B请求一些数据然后在服务A上继续执行。或者在游戏编程中一个技能分成分多段先执行一部分动作效果然后等待一段时间然后再继续。对于这种需要等待、而又不希望阻塞的情况我们一般使用回调callback的方式。下面举一个简单的例子
1 def do(a):
2 print do, a
3 CallBackMgr.callback(5, lambda a a: post_do(a))
4
5 def post_do(a):
6 print post_do, a 这里的CallBackMgr注册了一个5s后的时间5s之后再调用lambda函数可见一段逻辑被分裂到两个函数而且还需要上下文的传递如这里的参数a。我们用yield来修改一下这个例子yield返回值代表等待的时间。
1 yield_dec
2 def do(a):
3 print do, a
4 yield 5
5 print post_do, a 这里需要实现一个YieldManager 通过yield_dec这个decrator将do这个generator注册到YieldManager并在5s后调用next方法。Yield版本实现了和回调一样的功能但是看起来要清晰许多。下面给出一个简单的实现以供参考 # -*- coding:utf-8 -*-
import sys
# import Timer
import types
import timeclass YieldManager(object):def __init__(self, tick_delta 0.01):self.generator_dict {}# self._tick_timer Timer.addRepeatTimer(tick_delta, lambda: self.tick())def tick(self):cur time.time()for gene, t in self.generator_dict.items():if cur t:self._do_resume_genetator(gene,cur)def _do_resume_genetator(self,gene, cur ):try:self.on_generator_excute(gene, cur)except StopIteration,e:self.remove_generator(gene)except Exception, e:print unexcepet error, type(e)self.remove_generator(gene)def add_generator(self, gen, deadline):self.generator_dict[gen] deadlinedef remove_generator(self, gene):del self.generator_dict[gene]def on_generator_excute(self, gen, cur_time None):t gen.next()cur_time cur_time or time.time()self.add_generator(gen, t cur_time)g_yield_mgr YieldManager()def yield_dec(func):def _inner_func(*args, **kwargs):gen func(*args, **kwargs)if type(gen) is types.GeneratorType:g_yield_mgr.on_generator_excute(gen)return genreturn _inner_funcyield_dec
def do(a):print do, ayield 2.5print post_do, ayield 3print post_do again, aif __name__ __main__:do(1)for i in range(1, 10):print simulate a timer, %s seconds passed % itime.sleep(1)g_yield_mgr.tick() 注意事项 回到顶部 1Yield是不能嵌套的 1 def visit(data):2 for elem in data:3 if isinstance(elem, tuple) or isinstance(elem, list):4 visit(elem) # here value retuened is generator5 else:6 yield elem7 8 if __name__ __main__:9 for e in visit([1, 2, (3, 4), 5]):
10 print e 上面的代码访问嵌套序列里面的每一个元素我们期望的输出是1 2 3 4 5而实际输出是1 2 5 。为什么呢如注释所示visit是一个generator function所以第4行返回的是generator object而代码也没这个generator实例迭代。那么改改代码对这个临时的generator 进行迭代就行了。
def visit(data):for elem in data:if isinstance(elem, tuple) or isinstance(elem, list):for e in visit(elem):yield eelse:yield elem或者在python3.3中 可以使用yield from这个语法是在pep380加入的1 def visit(data):
2 for elem in data:
3 if isinstance(elem, tuple) or isinstance(elem, list):
4 yield from visit(elem)
5 else:
6 yield elem2generator function中使用return在python doc中明确提到是可以使用return的当generator执行到这里的时候抛出StopIteration异常。1 def gen_with_return(range_num):2 if range_num 0:3 return4 else:5 for i in xrange(range_num):6 yield i7 8 if __name__ __main__:9 print list(gen_with_return(-1))
10 print list(gen_with_return(1))但是generator function中的return是不能带任何返回值的1 def gen_with_return(range_num):
2 if range_num 0:
3 return 0
4 else:
5 for i in xrange(range_num):
6 yield i 上面的代码会报错SyntaxError: return with argument inside generator References
http://www.dabeaz.com/generators-uk/ https://www.python.org/dev/peps/pep-0380/ http://stackoverflow.com/questions/231767/what-does-the-yield-keyword-do http://stackoverflow.com/questions/15809296/python-syntaxerror-return-with-argument-inside-generator