简单来说可迭代对象包含迭代器,迭代器包含生成器。

可迭代对象和迭代器
迭代是重复反馈过程的活动,其目的通常是为了逼近所需目标或结果。每一次对过程的重复称为一次“迭代”,而每一次迭代得到的结果会作为下一次迭代的初始值。
在python中,可迭代的表象是对list、tuple、dict、set、str、range对象等类型的数据使用for循环依次从其中拿到数据进行使用,这个过程称做遍历,也叫迭代。
代码角度看是内部含有__iter__
方法的对象就是可迭代对象。__iter__
方法的作用就是返回一个迭代器对象。
for循环过程中具体的实现方式是什么
1
2
3x = [1, 2, 3] for i in x: print(i)

图片来源:https://www.cnblogs.com/eastonliu/p/9156418.html
- 调用可迭代对象的
__iter__
方法返回一个迭代器对象(iterator) - 不断调用迭代器的
__next__
方法返回元素 - 知道迭代完后,处理StopIteration异常
迭代器是实现了__next__
方法的对象。
所以定义可迭代对象必须实现__iter__
方法,定义迭代器必须实现__iter__
和__next__
方法。
判断一个对象是不是可迭代对象
1
2
3
4
5
6
7
8
9
10
11
12
13In [50]: from collections import Iterable In [51]: isinstance([], Iterable) Out[51]: True In [52]: isinstance({}, Iterable) Out[52]: True In [53]: isinstance('abc', Iterable) Out[53]: True In [55]: isinstance(100, Iterable) Out[55]: False
__iter__
和__next__
具体实现
__iter__
有两种写法:
写法一:用户可迭代对象,返回该可迭代对象的迭代器实例
写法二:用户迭代器的写法,返回self(迭代器本身),表示自身就是迭代器。
__next__
方法,返回迭代的每一步,实现该方法最后注意超出边界要抛出StopIteration异常。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25class Mylist: #定义可迭代对象类 def __init__(self, data): self.data = data #上边界 def __iter__(self): return MylistIterator(self.data) #返回迭代器实例 class MylistIterator: #定义迭代器类 def __init__(self, data): self.now = 0 #当前迭代值,初始为0 self.data = data #迭代器上边界 def __iter__(self): return self #返回迭代器本身 def __next__(self): #迭代器必须实现next方法 while self.now < self.data: self.now += 1 return self.now - 1 raise StopIteration #超出上边界,抛出异常 mylist = Mylist(5) #得到一个可迭代对象 print(type(mylist)) #返回该对象类型 mylist_iter = iter(mylist) #得到一个迭代器实例 print(type(mylist_iter)) # for i in mylist_iter: # print(i) next(mylist_iter)
返回值
1
2
3<class '__main__.Mylist'> <class '__main__.MylistIterator'> 0
迭代器就像一个懒加载的工厂,等到有人需要的时候才给它生成值返回,没调用的时候就处于休眠状态等待下一次调用。直到无元素可调用,返回StopIteration异常。
生成器
生成器是一种特殊的迭代器,生成器自动实现了“迭代器协议”(即__iter__
和__next__
方法),不需要手动实现两个方法,只需要一个yield关键字。
生成器在迭代过程中可以改变当前迭代值,而修改普通迭代器的当前迭代值会发生异常,影响程序的执行。
为什么要使用生成器:节省空间
通过列表生成式,我们可以直接创建一个列表,但是受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前几个元素,那么后面绝大多数元素占用的空间就白费了。
所以,如果列表可以按照某种算法推算出来,那我们是否可以在循环过程中不断推算出后面的元素呢?这样就不必创建完整的list,从而节省了大量的空间。在python中,这种一边循环一边计算的机制,称为生成器。
计算斐波那契的生成器
1
2
3
4
5
6
7def fibon(n): a = b = 1 for i in range(n): yield a a, b = b, a + b fibon(10) <generator object fibon at 0x00000280C97E22C8>
使用循环打印出来
1
2for i in fibon(10): print(i)
如果用函数来实现斐波那契数列
1
2
3
4
5
6
7
8
9def fibon(n): result = [] a = b = 1 for i in range(n): result.append(a) a, b = b, a + b return result fibon(10) [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
生成器特殊的地方在于函数体中没有return
关键字,函数的返回值是一个生成器对象。当执行f=fib()
返回的是一个生成器对象,此时函数体中的代码并不会执行,只有显示或隐示地调用next的时候才会真正执行里面的代码。
具有yield关键字的函数都是生成器,yield可以理解为return,返回后面的值给调用者。不同的是return返回后,函数会释放,而生成器则不会,在直接调用next方法或用for语句进行下一次迭代时,生成器会从yield下一句开始执行,直到遇到下一个yield。
生成器还有一个send方法,可以往生成器里的变量传值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15def fibon(n): a = b = 1 for i in range(n): c = yield a print(c) a, b = b, a + b a=fibon(10) print(next(a)) print(next(a)) print(next(a)) print(a.send(10)) print(next(a)) print(next(a)) print(next(a))
返回值
1
2
3
4
5
6
7
8
9
10
11
12
131 None 1 None 2 10 #这里的None被换成了10 3 None 5 None 8 None 13
要实现send方法有效果,必须得把yield表达式赋值一个一个变量,然后再打印。不然完全没有反应。可能还是暂时没弄懂。
生成器表达式
生成器表达式和列表推导式很像,就是把方括号换成小括号
1[i for i in range(10)] #列表推导式或解析式
返回
1[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
生成器表达式
1(i for i in range(10))
返回
1<generator object <genexpr> at 0x000002303BC9A0C8>
折腾了好久总觉得差点意思,就先把这个当做一个开头,后面再补充。
最后
以上就是等待钻石最近收集整理的关于html生成器_可迭代对象、迭代器和生成器(1)的全部内容,更多相关html生成器_可迭代对象、迭代器和生成器(1)内容请搜索靠谱客的其他文章。
发表评论 取消回复