10.1迭代器
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator
迭代器对象要求支持迭代器协议的对象,在Python中,支持迭代器协议就是实现对象的_ iter ()和 next _()的方法。
li = []
for i in range(1,11): #for后面要是可迭代对象
li.append(i)
print(li)
li1 = [i for i in range(1,11)] #列表推导式
print(li1)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
迭代器其实就是一个可迭代对象
>>> li = [1,2,3,4,5,6,7]
>>> n = iter(li) #将列表li变成了迭代器
>>> n
<list_iterator object at 0x0000000002EFAB70>
>>> type(n)
<class 'list_iterator'>
>>> next(n)
1
>>> next(n)
2
>>> next(n)
3
>>> next(n)
4
>>> next(n)
5
>>> next(n)
6
>>> next(n)
7
>>> next(n)
Traceback (most recent call last):
File "<pyshell#16>", line 1, in <module>
next(n)
StopIteration
迭代器其实就是一个容器,里面装有元素,不能直接查看,可以通过next()取出元素,直到把所有元素取出就停止迭代报错。
那既然迭代器是一个容器,那我们可以向从其他容器里取出特定的数据或者倒着取数据吗?
看下面可以看到list有很多方法支持我们从不同位置等特定的需求来取数据,但是我们发现迭代器却没有方法,所以我们无法对迭代器做任何的操作,只能遵从迭代器顺序的方式依次取出数据。
>>> li = [1,2,3,4,5,6,7]
>>> n = iter(li)
>>> dir(n)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__length_hint__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__']
>>> dir(list)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
注意:next(n) == n._ next _() 取出下一个元素
同理:iter(n) == n._ iter _() 返回迭代器对象本身
>>> li = [1,2,3,4]
>>> li = li.__iter__()
>>> li
<list_iterator object at 0x0000000002F774E0>
>>> next(li)
1
>>> li.__next__()
2
先来看下for循环到底发生了什么
li –>iter(li) –>i=next(li)
li = [1,2,3,4,5,6,7,8,9,10]
for i in li: #li -->iter(li) -->i=next(li)
print (i)
插播一个三目元算符:
>>> a = 1
>>> b = 2
>>> c = 3
>>> d = c if a else b #如果a!=0,d = c,else d=b
>>> d
3
>>> a = 0
>>> d = c if a else b
>>> d
2
10.2生成器
生成器都是迭代器对象,都可以使用next()和iter()方法(拥有迭代器类似的属性)。
迭代器和生成器都是容器,迭代器元素确定了,生成器没有确定。
>>> li = [i for i in range(1,11)]
>>> li
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> li = (i for i in range(1,11)) #[]-->>()转换成生成器
>>> li
<generator object <genexpr> at 0x0000000002F64780>
>>> type(li)
<class 'generator'>
#裴波那契数列
def fib(maxnum): #函数
n,a,b = 0,0,1
while n < maxnum:
print(b)
a,b = b,a+b
n += 1
def fib1(maxnum): #生成器函数用def定义用yield语法糖一次返回一个结果,返回的其实是生成器的迭代器,成为生成器
n,a,b = 0,0,1
while n < maxnum:
yield b #yield返回的是一个生成器,运行到yield会停止
a,b = b,a+b
n += 1
>>> fib(6)
1
1
2
3
5
8
>>> fib1(6) #因为fib1(b)利用yield返回了生成器,但是fib1没有传值的话就只是一个函数
<generator object fib1 at 0x0000000003193E60>
>>> type(fib)
<class 'function'>
>>> type(fib1)
<class 'function'>
>>> a = fib1(6)
>>> a
<generator object fib1 at 0x0000000003193E60>
>>> next(a)
1
>>> next(a)
1
>>> next(a)
2
>>> next(a)
3
看下面这个例子,生成器有个特性,就是遇到生成器程序会中断,next(生成器)就会继续运行下面
def ff():
print('天才第一步 1')
yield 2
print('确实纸尿裤 3')
yield 4
print('球王贝利代言 5')
yield (6)
结果:
>>> c = ff()
>>> c
<generator object ff at 0x0000000002EFFAF0>
>>> next(c)
天才第一步 1
2
>>> next(c)
确实纸尿裤 3
4
>>> next(c)
球王贝利代言 5
6
>>> next(c)
Traceback (most recent call last):
File "<pyshell#20>", line 1, in <module>
next(c)
StopIteration
再看一个例子:
def ff():
print('天才第一步 1')
yield 2
print('确实纸尿裤 3')
yield 4
print('球王贝利代言 5')
yield (6)
def fun(n):
i = 0
while i < n:
print('********',i)
yield i
i += 1
print ('++++',i)
def fff():
n=1
while True:
print(n)
yield n
n += 1
>>> fun(6)
<generator object fun at 0x0000000002EFFAF0>
>>> a = fun(6)
>>> a
<generator object fun at 0x0000000002F64888>
>>> next(a)
******** 0
0
>>> next(a)
++++ 1
******** 1
1
>>> next(a)
++++ 2
******** 2
2
>>> next(a)
++++ 3
******** 3
3
>>> next(a)
++++ 4
******** 4
4
>>> next(a)
++++ 5
******** 5
5
>>> next(a)
++++ 6
Traceback (most recent call last):
File "<pyshell#31>", line 1, in <module>
next(a)
StopIteration
生成器和迭代器不一样的是,迭代器其实就是一个容器用来存储元素,但是生成器不是用来存储元素的,而是一般用来存储生成元素的算法,然后一边生成一边运行,也就是我们通过生成器一个个去生成元素,而不是一下子生成全部的元素,这样对内存的占用会比较大。
比如我们要生成一个list,因为内存是有限的,列表的容量就是有限的,而且假如我们创建生成了一个元素特别多的list,但是我们只需要访问前面几个元素的话,那后面那些空间就是浪费的,所以这样我们可以利用生成器,一边循环一边计算生成元素,不用创建完整的list,从而节省了大量的空间。
小结:
迭代器:
1. 可以实现next(),iter()方法(next(a) == a._ next _())
2. 迭代器是一个容器,用来保留元素,利用next函数来取出元素,一个取出一个取完报错
3. iter函数返回的是迭代器对象本身,可以使用这个函数来将其他转换成迭代器
4. 创建迭代器的方法:iter函数
生成器:
1. 生成器是一个特殊的迭代器,支持迭代器协议,也就是可以实现next(),iter()方法
2. 生成器也是一个容器,只不过它保存的不是元素而是生成元素的算法,一边生成一边运行
3. 生成器用def定义用yield语法糖来返回生成器(生成器其实是生成器的迭代器)
4. 程序遇到yield就会暂停,然后调用生成器的next()方法返回生成的元素的同时程序会继续往下运行
5、创建生成器的方法:yield语法糖、(i for i in range(1,11))(列表生成器中括号改成小括号 )
6、相比较直接生成一个完整list,生成器占用内存很小