生成器,生成器表达式。
今天在看python的时候接触到了生成器和生成器表达式的概念,感觉有点迷糊。特此总结记忆一下。
生成器的出现可以说是基于下面一种需求。首先,我们来看一端python程序。
[root@racnode1 tmp]# cat /tmp/test aaaaa bbbbbbbbb ccccc ddddd [root@racnode1 tmp]# python Python 2.6.6 (r266:84292, Dec 20 2012, 15:52:58) [GCC 4.4.7 20120313 (Red Hat 4.4.7-3)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> f = open('/tmp/test') >>> allLineLens=[len(x.strip()) for x in f] >>> f.close() >>> max(allLineLens) 9
首先我们看到一个很简单的文件/tmp/test 共有四行,第二行最长有9个字符。 然后我们用一段python程序来检测该文件最长的行长度是多少。
f=open('/tmp/test')是打开这个文件。
allLineLens=[len(x.strip()) for x in f] 是通过一个列表解析来获得一个列表。该列表中的每一个元素都是f中每一行的长度
f.close()是关闭文件
max(allLineLens)是找出该列表中最大的值。
这段程序清晰明了,但是有一个问题,如果这个文件非常大,那么我们要得到的列表可能就会非常大。这样会占用内存很大。这里我们就可以用到生成器表达式来解决这个问题。生成器表达式的语法和列表表达式一样。只是不用[],而且它生成的不是列表而是一个生成器。什么是生成器我们稍后解释。
列表解析: [expr for iter_var in iterable if cond_expr] 生成器表达式: (expr for iter_var in iterable if cond_expr)
上面的例子用生成器表达式来改写就是:
>>> f = open('/tmp/test') >>> max( len(x.strip()) for x in f) 9
这段代码与上面的区别是 len(x.strip()) for x in f返回的不是一个列表而是一个生成器对象。生成器占用内存非常小。所以就解决了我们之前列表占用内存过大的问题。
那么生成器是什么呢?
生成器是一次生成一个值的特殊类型函数。可以将其视为可恢复函数。调用该函数将返回一个可用于生成连续 x 值的生成器【Generator】
简单的说就是在函数的执行过程中,yield语句会把你需要的值返回给调用生成器的地方,然后退出函数,下一次调用生成器函数的时候又从上次中断的地方开始执行,而生成器内的所有变量参数都会被保存下来供下一次使用。
- >>> def fib(max):
- a, b = 0, 1
- while a < max:
- yield a
- a, b = b, a + b
- >>> for i in fib(1000):
- print(i)
- 0
- 1
- 1
- 2
- 3
- 5
- 8
- 13
- 21
- 34
- 55
- 89
- 144
- 233
- 377
- 610
- 987
- >>>f = fib(1000)
- >>>f.next() #python 3.0 要写成f.__next__() 否则出错
- 0
- >>>f.next()
- 1
- >>>f.next()
- 1
- >>>f.next()
- 2
#python 3.0 要写成f.__next__() 否则出错 AttributeError: 'generator' object has no attribute 'next'
在函数fib(max)内定义了一个生成器,但是对fib(max)的调用永远只能获得一个单独的生成器对象,而不是执行函数里面的语句,这个对象(generator object)包含了函数的原始代码和函数调用的状态,这状态包括函数中变量值以及当前的执行点——函数在yield语句处暂停(suspended),返回当前的值并储存函数的调用状态,当需要下一个条目(item)时,可以再次调用next,从函数上次停止的状态继续执行,知道下一个yield语句。
生成器和函数的主要区别在于函数 return a value,生成器 yield a value同时标记或记忆 point of the yield 以便于在下次调用时从标记点恢复执行。 yield
使函数转换成生成器,而生成器反过来又返回迭代器。
有三种方式告诉循环生成器中没有更多的内容:
- 执行到函数的末尾("fall off the end")
- 用一个return语句(它可能不会返回任何值)
- 抛出StopIteration异常
总的来说生成器是一类特殊 迭代器。 一个产生值的函数 yield
是一种产生一个迭代器却不需要构建迭代器的精密小巧的方法