Python--生成器与迭代器

生成器(generator)

在看生成器之前我们先来看一下列表生成式。

如果我们想得到一个12,22,32…… 102组成的列表,我们可以考虑下面的做法:

1 a=[x*x for x in range(1,11)]
2 print(a)

输出:

——————————

[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

——————————

这是Python的简洁的体现之一,我们可以用这种方式快速得到一个列表。其实他还有很多玩法:

1 import pprint
2 a=[(x,y )for x in range(3) for y in "abc"]
3 pprint.pprint(a)#pprint()是让打印的结果更美观,不至于太长
4
5 b=[abs(x) for x in range(-5,5)]
6 print(b)

输出:

————————

[(0, ‘a‘),
 (0, ‘b‘),
 (0, ‘c‘),
 (1, ‘a‘),
 (1, ‘b‘),
 (1, ‘c‘),
 (2, ‘a‘),
 (2, ‘b’),
 (2, ‘c‘)]
[5, 4, 3, 2, 1, 0, 1, 2, 3, 4]
————————

下面就让我们正式看一下生成器。得到一个生成器的方法其实很简单,我们只需要把列表生成式里的[]改成()即可,如下:

1 a=(x*x for x in range(1,11))
2 print(a)

输出:

——————————

<generator object <genexpr> at 0x0398BAA8>

——————————

我们打印a,得到这样的结果,说明a是一个生成器,他的内存地址为:0x0398BAA8。这里显然还没有生成a中的数据,和列表生成式有很大不同。列表生成式在运行时会在内存中产生列表中的所有数据(即使我们并没有用到这些数据),而当我们没有使用到数据时,生成器则并没有生成任何一个数据。看起来生成器相当的懒惰啊。没错,我们把生成器又叫做惰性序列。但是这里的懒惰是褒义的,因为他节省了内存空间。

那怎样才能让生成器生成数据呢?答案:next

 1 a=(x*x for x in range(1,11))
 2 print(a.__next__())
 3 print(a.__next__())
 4 print(a.__next__())
 5 print(a.__next__())
 6 print(a.__next__())
 7 print(a.__next__())
 8 print(a.__next__())
 9 print(a.__next__())
10 print(a.__next__())
11 print(a.__next__())
12 print(a.__next__())
13 #上面的11个a.__next()__改为11个next(a)效果不变

输出:

——————

1

4

9

16

25

36

49

56

81

100

error:StopIteration

——————

我们看到生成器中有10个数字,我们调用了11次next(或__next__),每调用一次,生成器生成下一个值并且释放上一个值,当已经生成所有的之后,就抛出StopIteration的错误。可以看到这样对计算机的内存是极大的节省。但是这样每次都调用next的方式未必太傻叉点了吧。有更好的方法吗?答案:for循环。

1 a=(x*x for x in range(1,11))
2 for i in a:
3     print(i)

输出:

————————

1
4
9
16
25
36
49
64
81
100

————————

这样就优雅多了。

上面的那种用把列表生成式的中括号改成圆括号得到的生成器的原理是什么呢?为什么他就可以惰性的产生数据呢?这就涉及到yield了。

我们先看下面的一个函数:

1 def doublenum(n):
2     for i in range(1,n+1):
3         if i%2==0:
4            print(i)
5 doublenum(10)

输出结果:

————————

2

4

6

8

10

————————

下面我把他改一下:

1 def doublenum(n):
2     for i in range(1,n+1):
3         if i%2==0:
4            yield i #把原来的print(i)改成了yield i
5 print(doublenum(10))

输出:

————————

<generator object doublenum at 0x02AE7698>

————————

What happened!!!它变成了一个生成器!,我们看看可以用next来生成数据吗?

 1 def doublenum(n):
 2     for i in range(1,n+1):
 3         if i%2==0:
 4            yield(i)
 5 g=doublenum(10)
 6 print("循环的方式:")
 7 for i in g:
 8     print(i)
 9 print("\nnext的方式:")
10 print(next(g))
11 print(next(g))
12 print(next(g))
13 print(next(g))
14 print(next(g))

输出:

————————

循环的方式:

2

4

6

8

10

next的方式:

2

4

6

8

10

StopIteration

————————

可见他确实变成了生成器了。

其实生成器就是含有yield的函数或者类(明白了吗?上面的函数含有yield,所以他编程了生成器。)

下面为了让你更明白生成器,我来写一个有意思的东西:

 1 def  consumer(name):
 2     print("%s准备吃包子了……"%name)
 3     while True:
 4         baozi=yield
 5         print("%s已经吃完%s了"%(name,baozi))
 6 def producer(baozi_name1,baozi_name2):
 7     xiaoming=consumer("小明")
 8     xiaohong=consumer("小红")
 9     xiaoming.__next__()
10     xiaohong.__next__()
11     print("我做了一个%s和一个%s,分别送给小明和小红"%(baozi_name1,baozi_name2))
12     xiaoming.send(baozi_name1)
13     xiaohong.send(baozi_name2)
14
15 for i in range(10):
16     producer("韭菜包","奶黄包")

输出:

————————

小明准备吃包子了……
小红准备吃包子了……
我做了一个韭菜包和一个奶黄包,分别送给小明和小红
小明已经吃完韭菜包了
小红已经吃完奶黄包了
小明准备吃包子了……
小红准备吃包子了……
我做了一个韭菜包和一个奶黄包,分别送给小明和小红
小明已经吃完韭菜包了
小红已经吃完奶黄包了
小明准备吃包子了……
小红准备吃包子了……
我做了一个韭菜包和一个奶黄包,分别送给小明和小红
小明已经吃完韭菜包了
小红已经吃完奶黄包了
小明准备吃包子了……
小红准备吃包子了……
我做了一个韭菜包和一个奶黄包,分别送给小明和小红
小明已经吃完韭菜包了
小红已经吃完奶黄包了
小明准备吃包子了……
小红准备吃包子了……
我做了一个韭菜包和一个奶黄包,分别送给小明和小红
小明已经吃完韭菜包了
小红已经吃完奶黄包了
小明准备吃包子了……
小红准备吃包子了……
我做了一个韭菜包和一个奶黄包,分别送给小明和小红
小明已经吃完韭菜包了
小红已经吃完奶黄包了
小明准备吃包子了……
小红准备吃包子了……
我做了一个韭菜包和一个奶黄包,分别送给小明和小红
小明已经吃完韭菜包了
小红已经吃完奶黄包了
小明准备吃包子了……
小红准备吃包子了……
我做了一个韭菜包和一个奶黄包,分别送给小明和小红
小明已经吃完韭菜包了
小红已经吃完奶黄包了
小明准备吃包子了……
小红准备吃包子了……
我做了一个韭菜包和一个奶黄包,分别送给小明和小红
小明已经吃完韭菜包了
小红已经吃完奶黄包了
小明准备吃包子了……
小红准备吃包子了……
我做了一个韭菜包和一个奶黄包,分别送给小明和小红
小明已经吃完韭菜包了
小红已经吃完奶黄包了

————————

上面是一个生产者消费者模型,生产者不停的生产包子,消费者不停的吃包子,这样程序就在两个函数之间交替执行,由于运行速度非常快,就给了用户一种并行的幻觉。这就在单线程的情形之下实现了“假并行”。这其实叫做协程。注意send函数有两个功能:

1.重新唤醒消费者,使其继续执行。

2.将参数发送给yield,然后yield将其赋值给包子变量。

总的来说:生成器就是一种惰性执行的函数,每一次的next都将使函数执行到yield,然后停止,直到下一个next的唤醒,唤醒之后有接着上一次的yield处继续执行。

迭代器(Iterator)

Python中的迭代器与c++中的迭代器有很大的不同,不可将二者进行类比。在Python中判断一个对象是否为迭代器就是看这个对象是否拥有next()(或__next__())这一方法,有即是,没有就不是。所以上面讲的生成器就是迭代器。

对于迭代器的掌握大家掌握上面这一点即可,主要掌握生成器。

总结

yield可以实现协程,主要要理解yield的用法。

时间: 2024-10-05 10:47:05

Python--生成器与迭代器的相关文章

Python生成器、迭代器、装饰器

Python迭代器 迭代器是访问集合内元素的一种方式.迭代器对象从集合的第一个元素开始访问,直到所有的元素都被访问一遍后结束. 迭代器不能回退,只能往前进行迭代.这并不是什么很大的缺点,因为人们几乎不需要在迭代途中进行回退操作. 常用的迭代方法有 .next()方法 for..in..方法 迭代器通俗的理解就是遍历集合内的所有元素 python生成器 这里先说简单的使用,然后再说自己创建生成器 range:生成一个list range(1,5)结果为:[1,2,3,4] xrange:生成一个x

python 生成器和迭代器有这篇就够了

本节主要记录一下列表生成式,生成器和迭代器的知识点 列表生成器 首先举个例子 现在有个需求,看列表 [0,1,2,3,4,5,6,7,8,9],要求你把列表里面的每个值加1,你怎么实现呢? 方法一(简单): info = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] b = [] # for index,i in enumerate(info): # print(i+1) # b.append(i+1) # print(b) for index,i in enumerate(in

python生成器和迭代器

本节主要记录一下列表生成式,生成器和迭代器的知识点 列表生成器 首先举个例子 现在有个需求,看列表 [0,1,2,3,4,5,6,7,8,9],要求你把列表里面的每个值加1,你怎么实现呢? 方法一(简单): info = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] b = [] # for index,i in enumerate(info): # print(i+1) # b.append(i+1) # print(b) for index,i in enumerate(in

python 生成器和迭代器

迭代器协议 1.迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个Stoplteration异常,以终止迭代(只能往后走不能往前退) 2.可迭代对象:实现了迭代器协议的对象(如何实现:对象内部定义一个__iter__()方法) 3.协议是一种约定,可迭代对象实现了迭代器协议,python的内部工具(如for循环,sum,min,max函数等)使用迭代器协议访问对象. 迭代:重复做一件事 iterable(可迭代)对象 支持每次返回自己所包含的一个成员的

python 生成器和迭代器介绍

在正式接触生成器之前,我们先来了解一些概念 容器(container) 容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个迭代获取,可以用in.not in关键字判断元素是否包含在容器中.通常这类数据结构把所有的元素存储在内存中(也有一些特殊的存在). Python中常见的容器对象: list ,deque, ... set, frozensets, ... dict, defaultdict, OrderedDict, Counter, ... tuple, nametuple,

Python生成器、迭代器、可迭代对象

把一个列表[]改成()就创建了一个生成器:generator,generator保存的是算法. 可以用于for循环的数据类型:1.集合类型:list tuple dict set str2.generator 可以直接作用于for循环的对象统称为:可迭代对象(Iterable) from collections import Iterable print(isinstance([],Iterable)) print(isinstance({},Iterable)) print(isinstanc

python生成器与迭代器

生成器: 用列表生成式生成一个列表 [ i*2 for i in range(10) ] 这就是一个列表生成式.列表生成式使得创建列表代码变得简洁.但是,如果一个列表很大,这样创建就比较耗内存了.如果列表元素可以按照某种算法推算出来,那我们就可以在循环过程中不断推算出后续的元素,这样就不用创建一个完整的list.从而节省内存空间.在python中这种一边循环一边计算的机制,就称为生成器(generator). 要创建一个generator,有很多方法,第一种方法很简单,只需要把一个列表生成式的[

python生成器及迭代器

一.迭代器 迭代器是访问集合元素的一种方式 迭代器是一个可以记住遍历的位置的对象. 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退. 迭代器有两个基本的方法:iter() 和 next() 字符串,列表或元组对象都可用于创建迭代器 特点: 访问者不需要关心迭代器内部的结构,仅需通过next()方法不断去取下一个内容 不能随机访问集合中的某个值 ,只能从头到尾依次访问 访问到一半时不能往回退 便于循环比较大的数据集合,节省内存 >>> li = [

Python 生成器&迭代器

Python 生成器 带有 yield 的函数在 Python 中被称之为 generator(生成器),用斐波那契数列: def fab(max):     n, a, b = 0, 0, 1     while n < max:         yield b         a, b = b, a + b         n = n + 1 执行: 1 2 3 4 5 6 7 8 9 >>> for n in fab(5):     print n 1 1 2 3 5 简单地

Python之路【第六篇】:Python基础(22)——生成器和迭代器

迭代器 迭 代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退,不过这也没什么,因为人们 很少在迭代途中往后退.另外,迭代器的一大优点是不要求事先准备好整个迭代过程中所有的元素.迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或之 后,元素可以不存在或者被销毁.这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件. 特点: 访问者不需要关心迭代器内部的结构,仅需通过__next()__(Python2.x