1.容器、可迭代对象、迭代器、生成器概念
1. 容器:存储许多元素的数据结构。通常存储在内存(迭代器、生成器是特例)可以使用in来判断某个元素是否在存在的对象都是容器
举个例子:容器就像一个箱子,里面可以存放许多东西,我可以往这个箱子存取东西,可以判断这个箱子是否有某样东西
2.可迭代对象:可以使用iter()变成迭代器的对象都是可迭代对象,大部分容器都是可迭代对象(str,set,list,tuple,打开状态的files,sockets等等)
3.迭代器:它是一个带状态的对象,保存当前记录状态,当使用next()函数调用时,可以返回容器的下一个值。如果容器中没有更多元素了,则抛出StopIteration异常.
任何实现了__iter__() and next()方法的对象都是迭代器,__iter__返回迭代器自身,__next__对象返回下一个值。生成器是另一种形态的迭代器
迭代器就是实现了工厂模式的对象,它在你每次你询问要下一个值的时候给你返回。比如itertools函数返回的都是迭代器对象。
4.生成器:使用yield关键字,python都会把这个函数视为一个生成器.它返回了一个可迭代的对象,可以使用next()访问它里面的值了
5.如何使用迭代器呢?
1.方式是使用for 。。 in 。语法糖结构来遍历迭代器。
for默认的会调用__iter__返回一个迭代对象,然后调用next()方法调用迭代对象的__next__方法获取下一个值。
如果最后没有值了,迭代器会抛出一个异常,for会捕捉处理这个异常,退出循环.
2.先获取迭代器对象,如使用it = iter(list)方法,在调用next(it)获取下一个值。最后没值了会抛出异常(StopIteration)
下面一张图来说明他们之间的关系
2.具体实例来演示上面的内容
以斐波那契数列来举例
1.普通的函数实现
# 普通函数斐波那契数列
def fib1(n):
pre, cur = 0, 1
i = 0
while i < n:
print(cur, end=\' \')
pre, cur = cur, cur + pre
i += 1
调用: fib1(10)
输出:1 1 2 3 5 8 13 21 34 55
2.普通的函数实现改进
# 普通函数斐波那契数列改进
def fib1(n):
result = []
pre, cur = 0, 1
i = 0
while i < n:
result.append(cur)
pre, cur = cur, cur + pre
i += 1
return result
调用:lst = fib1(10)
print(lst)
输出:[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
3.自定义一个可迭代的类来实现斐波那契数列
class Fib:
def __init__(self, stop):
self.stop = stop
self.pre = 0
self.cur = 1
self.start = 0
print(\'init finish\')
def __iter__(self):
print(\'return iter\')
return self
def __next__(self):
print(\'return next value\')
if self.start < self.stop:
self.start += 1
x = self.cur
self.pre, self.cur = self.cur, self.cur + self.pre
return x
else:
raise StopIteration()
初始化类,得到一个类实例对象f,可以看到"init finish"打印出来了
In [1]: f = Fib(10)
Out[1]:init finish
我们知道一个可迭代对象可以通过iter()函数转换成一个迭代器,
从下面的调用我们可以看出,当我们使用iter()函数时,它默认去调用函数的__iter__()函数,返回迭代对象
In [2]: it = iter(f)
Out[2]:return iter
当我们获取了迭代对象,我们调用next()函数来获取我们想要的值,
那么next()有做了哪些动作呢
1.它调用类中的__next__()方法
2.我们__next__()方法,返回当前值,并保存当前值记录状态和算出下一次调用要返回的值
In [3]: next(it)
Out[30]:return next value
Out[30]: 1
In [3]: next(it)
Out[30]:return next value
Out[30]: 2
如果一直执行next(),它会一直返回值,直到抛出StopIteration()异常
4.使用生成器
使用生成器,我们有两种形式:
1.创建生成器函数,使用yield关键字
2.使用生成器表达式
4.1创建生成器函数
# 使用yield实现斐波那契数列
def fib(n):
pre, cur = 0, 1
i = 0
while i < n:
print(\'call this\')
yield cur
pre, cur = cur, pre + cur
i += 1
print(\'here\')
从下面的执行结果我们可以看出,使用关键字yield后,调用fib(10)它的返回值是一个生成器
In [33]: f = fib(10)
In [34]: type(f)
Out[34]: generator
从上面图我们知道generator is iterator,我们可以使用next()函数获取值,可以看出我们没执行一次next(),
好像我们的程序就在yield返回cur,然后停到这不执行,直到下次调用next()函数又继续往下走,不停重复这个过程
In [1]: next(f)
call this
Out[1]: 1
In [2]: next(f)
here
call this
Out[2]: 2
In [3]: next(f)
here
call this
Out[3]: 3
4.2生成器表达式实现斐波那契数列
# 从下面我们可以看出,使用()后a是一个generator类型
In [1]: a = ( x for x in fib3(10))
In [2]: type(a)
Out[2]: generator
In [3]: next(a)
call this
Out[3]: 1
In [4]: next(a)
here
call this
Out[4]: 1
In [5]: next(a)
here
call this
Out[5]: 2
5.对于可迭代对象遍历,我们一定会想到for循环
下面具体解释下for循环:
1.for循环的一般格式如下:
for iter_var in iterable:
suite_to_repeat
从上面可以看出,只要是可迭代对象都可以使用
它的里面到底做了些什么呢?
举个例子吧,我们看看for循环它到底做了哪些工作
class Fib:
def __init__(self, stop):
self.stop = stop
self.pre = 0
self.cur = 1
self.start = 0
print(\'init finish\')
def __iter__(self):
print(\'return iter\')
return self
def __next__(self):
print(\'return next value\')
if self.start < self.stop:
self.start += 1
x = self.cur
self.pre, self.cur = self.cur, self.cur + self.pre
return x
else:
raise StopIteration()
对一个可迭代对象进行迭代操作
def fortest():
for x in Fib(4):
print(x)
fortest()
import dis
dis.dis(fortest)
打印以下信息:
init finish
return iter
return next value
1
return next value
1
return next value
2
return next value
3
反编译结果
108 0 SETUP_LOOP 24 (to 26)
2 LOAD_GLOBAL 0 (Fib)
4 LOAD_CONST 1 (4)
6 CALL_FUNCTION 1
8 GET_ITER
>> 10 FOR_ITER 12 (to 24)
12 STORE_FAST 0 (x)
109 14 LOAD_GLOBAL 1 (print)
16 LOAD_FAST 0 (x)
18 CALL_FUNCTION 1
20 POP_TOP
22 JUMP_ABSOLUTE 10
>> 24 POP_BLOCK
>> 26 LOAD_CONST 0 (None)
28 RETURN_VALUE
可以看出在for内部有两个很重要的指令GET_ITER,FOR_ITER,
GET_ITER相当于调用iter(Fib(4),返回对象迭代器it
FOR_ITER相当于调用next(it),调用类内部的__next()__()方法获取值,一直循环这个过程,直到遇到抛出的StopIteration()异常