目录
- 导语
- 可迭代对象
- 一、概念
- 二、可作用于for循环的可迭代对象
- 三、自定义可迭代对象实例
- 四、可迭代对象的for循环机制
- 迭代器
- 一、概念
- 二、内部实现方法
- 三、与可迭代对象的比较
- 三、实例
- 四、迭代器的for循环机制
- 生成器
- 一、概念
- 二、生成器表达式
- 三、生成器函数
- 四、yield
- 总结
导语
在做爬虫的时候用到了生成器的相关知识,当时就觉得生成器太厉害了,超级节省空间,于时便去详细的学习了一下生成器的知识,顺便把迭代器也一起学习了下,网上的讲解挺多的,这里我也总结一下,如有错误,敬请指正。
可迭代对象
一、概念
可迭代对象内部要么实现了__iter__()方法,要么实现了 __getitem__() 方法。
二、可作用于for循环的可迭代对象
- 由可迭代对象的概念可以知道,实现了__iter__()方法或__getitem__() 方法便是可迭代对象,然而并不是所有可迭代对象都可直接作用于for循环。(例子后面会讲到)
- 可直接作用与for循环的可迭代对象特点:要么实现了能返回迭代器的__iter__()方法,要么实现了 __getitem__() 方法而且其参数是从零开始的索引。
(这个网上有很多不同的见解,这里只说了我的理解,我们通常说的可迭代对象应该是指可作用于for循环的可迭代对象)
三、自定义可迭代对象实例
- 实现__iter__()方法:
1)Iterable(可迭代)类型:
例一:
1
2
3
4
5
6
7
8
9
10
11
12
13
14>>> class B: ... def __init__(self): ... pass ... def __iter__(self): ... pass ... >>> z = B() >>> isinstance(z, Iterable) True >>> iter(z) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: iter() returned non-iterator of type 'NoneType'
: : 例二:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20>>> class C: ... def __init__(self): ... self.List = [1, 3, 5] ... def __iter__(self): ... return self ... >>> I = C() >>> isinstance(I, Iterable) True >>> for i in I: ... print(i) ... Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: iter() returned non-iterator of type 'C' >>> iter(I) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: iter() returned non-iterator of type 'C'
::上面都实现了__iter__()方法,且都被判断为Iterable类型,但是__iter__()方法却没有返回迭代器,于是不能作用于for循环,故而不可迭代。
::2)可作用于for循环的可迭代对象:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21>>> class Score: ... def __init__(self): ... self.score = [1, 3, 4] ... def __iter__(self): ... return iter(self.score) ... >>> s = Score() >>> isinstance(s, Iterable) True >>> isinstance(s, Iterator) False >>> for i in s: ... print(i) ... 1 3 4 >>> iter(s) <list_iterator object at 0x000001585F4B09E8> >>>
::上面的__iter__()方法内部有个python内置函数iter(),它的作用是把一个可迭代对象转化为一个迭代器,故__iter__()方法返回的是一个迭代器,因此它是可以作用于for循环的可迭代对象,但却不是一个迭代器,后面我们会讲到。
- 实现__getitem__()方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29>>> class words: ... def __init__(self, word): ... self.word = word ... def __getitem__(self, index): ... return self.word[index] ... >>> w = words('Hello World') >>> isinstance(w, Iterable) False >>> for i in w: ... print(i) ... H e l l o W o r l d >>> isinstance(w, Iterator) False >>> iter(w) <iterator object at 0x000001585F4B09E8> >>>
::上面代码没有实现 __iter__() 方法,但是实现了 __getitem__() 方法,而且其参数是从零开始的索引,Python 会创建一个迭代器,尝试按顺序(从索引 0 开始)获取元素。
: :从上面的结果可以看出,实现了 __getitem__() 方法, 它并不是一个Iterable类型,但是它却是一个可作用于for循环的可迭代对象,且能够用iter()方法转化为迭代器。(这里可能有些矛盾,Iterable不就是可迭代对象的意思吗?那为什么它不是Iterable类型呢?我的理解是,从概念可以看出,可迭代对象既包括了Iterable类型,又包括实现了__getitem__() 方法的类。)
四、可迭代对象的for循环机制
从上面的分析我们可以大致了解了在可迭代对象下的for循环机制:
- 先判断对象是否为可迭代对象(等价于判断有没有 __iter__() 或 __getitem__() 方法),没有的话直接报错,抛出TypeError异常。
- 这里for循环兼容两种机制:有 __iter__() 的话,调用 __iter__() 方法,返回一个迭代器。当for发现如果对象没有 __iter__() ,但是实现了 __getitem__(),会改用下标迭代的方式。
(iter()方法也会处理这种情况,在不存在 __iter__()的时候,返回一个下标迭代的iterator对象来代替。这也就解释了上面的例子:实现了 __getitem__()方法,不是一个Iterable类型,却可以调用iter()方法转化为一个Iterator类型) - 在python内部不断地调用迭代器的__next__()方法,每次按序返回迭代器中的一个值。
- 迭代到最后没有元素时,就抛出异常 StopIteration,for循环会自动处理这个异常终止循环。
迭代器
一、概念
- 迭代也便是循环,是访问集合元素的一种方式
- 迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
二、内部实现方法
::迭代器实现了两个基本方法:__iter__() 与 __next__() 。
:: __iter__() :返回一个带有 __next__()方法的对象,也即迭代器本身。
:: __next__() :该方法将逐一访问容器中的元素。 当元素用尽时, __next__()将引发 StopIteration 异常,可以使用 内置函数next()来调用 __next__() 方法。
三、与可迭代对象的比较
- 可迭代对象有个 __iter__ ()方法,每次都实例化一个新的迭代器。这样就可以保证不同的迭代过程不会互相影响。
- 而迭代器实现 __iter__() 方法,返回迭代器本身,因此只能遍历一次,此外还要实现 __next__ ()方法,返回下一个元素
- 可作用于for循环或者实现__getitem()__()的可迭代对象可通过内置函数iter()转化为迭代器。
- 迭代器一定是可迭代对象,因为它实现了__iter__()方法;
三、实例
- 自定义迭代器的构造:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26>>> class MyIter(object): ... def __init__(self, myList): ... self.myList = myList ... self.index = 0 ... def __iter__(self): ... return self ... def __next__(self): ... if self.index != len(self.myList): ... data = self.myList[self.index] ... self.index += 1 ... return data ... else: ... raise StopIteration ... >>> m = MyIter([1, 3, 5]) >>> isinstance(m, Iterable) True >>> isinstance(m, Iterator) True >>> for i in m: ... print(i) ... 1 3 5
- 可迭代对象转化为迭代器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30>>> test1 = [1, 3, 5] >>> isinstance(test1, Iterable) True >>> isinstance(test1, Iterator) False >>> test3 = 'hello' >>> isinstance(test3, Iterable) True >>> isinstance(test3, Iterator) False >>> test4 = {7, 8, 9} >>> isinstance(test4, Iterable) True >>> isinstance(test4, Iterator) False >>> test5 = {'x': 10, 'y': 11, 'z': 12} >>> isinstance(test5, Iterable) True >>> isinstance(test5, Iterator) False >>> test6 = open('E:/test6.txt', 'r') >>> isinstance(test6, Iterable) True >>> isinstance(test6, Iterator) True >>> isinstance(iter(test1), Iterator) True >>> isinstance(iter(test5), Iterator) True
::从上面可以看出,基本数据类型:list、tuple、str、set、dict都是可迭代对象,却不是迭代器,但可以通过iter()方法转化为迭代器。
::但不止上面的类型,只要可作用于for循环或者实现__getitem()__()的可迭代对象可通过内置函数iter()转化为迭代器
四、迭代器的for循环机制
- 调用 __iter__()方法,返回自身self,也就是返回迭代器
- 不断地调用迭代器的next()方法,每次按序返回迭代器中的一个值
- 迭代到最后没有元素时,就抛出异常 StopIteration
生成器
一、概念
- 生成器(generator )是一个用于创建迭代器的简单而强大的工具,可以这么说,生成器是一种特殊的迭代器,自然也是一种可迭代对象。其内部实现了__iter__()和__next()__()方法
- 生成器函数( generator iterator ):它看起来很像普通函数,不同点在于其包含 yield 表达式以便产生一系列值供给 for-循环使用或是通过 next() 函数逐一获取。
- 生成器迭代器(generator iterator):generator 函数所创建的对象。
每个 yield 会临时暂停处理,记住当前位置执行状态(包括局部变量和挂起的 try 语句)。当该 生成器迭代器恢复时,它会从离开位置继续执行(这与每次调用都从新开始的普通函数差别很大)。 - 生成器表达式(generator expression ):返回一个迭代器的表达式。 它看起来很像普通表达式后面带有定义了一个循环变量、范围的 for 子句,以及一个可选的 if 子句。
注意:我们通常说的生成器是指生成器函数
二、生成器表达式
- 通过列表推导式语法,只不过将[]改为(),但这却不是一个元组推导式,元组没有推导式。
- 示例:
1
2
3
4
5
6
7
8
9
10
11>>> gen = (x for x in range(4)) >>> gen <generator object <genexpr> at 0x000001585F458D68> >>> for i in gen: ... print(i) ... 0 1 2 3
再看下一个例子,注意与上面的比较:
1
2
3
4
5
6
7
8
9
10>>> gen = (x for x in range(4)) >>> gen <generator object <genexpr> at 0x000001585F458DE0> >>> tuple(gen) (0, 1, 2, 3) >>> for i in gen: ... print(i) ... >>>
这里用for循环并没有取到值,因为生成器也是迭代器,故而其值只能使用一次,前面我们用了tuple()函数将生成器转化为了元组,相当于使用了一次生成器,故而第二次取值时,已经没有值了。
3.特点:
生成器表达式能做的事情列表推导式基本都能处理,只不过在需要处理的序列比较大时,列表推导比较费内存,生成器的优势也就体现出来了。
三、生成器函数
- 在函数中如果出现了yield关键字,那么该函数就不再是普通函数,而是生成器函数。
- 示例:
利用生成器实现斐波那契数列(除第一个和第二个数外,任何一个数都可以由前两个相加得到):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15>>> def fib(max): ... a, b, n = 0, 1, 0 ... while n < max: ... yield b ... a, b = b, a+b ... n += 1 ... >>> f = fib(4) >>> f <generator object fib at 0x000001585F458D68> >>> for i in f: ... print(i, end=' ') ... 1 1 2 3
::内部实现:在 for 循环执行时,每次循环都会执行 fib 函数内部的代码,执行到 yield b 时,fib 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。
- 特点:
生成器函数可以生产一个无限的序列,这是列表根本无法处理的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22>>> def even(): ... n = 0 ... while True: ... yield n ... n += 2 ... >>> e = even() >>> e <generator object even at 0x000001585F458E58> >>> count = 0 >>> for i in e: ... if count >4: ... break ... print(i) ... count += 1 ... 0 2 4 6 8
这里我们做了循环终止条件,故不会一直输出,使用生成器不会将所有数据取出来存入内存中,而是返回了一个对象,可以通过对象获取数据;用多少取多少,可以节省内存空间。
四、yield
关于yield的详细知识这里就不说了,推荐一篇浅显易懂的博文:python中yield的用法详解——最简单,最清晰的解释:https://blog.csdn.net/mieleizhi0522/article/details/82142856
总结
网上找的一幅图片,总结得很好:
最后
以上就是激情草莓最近收集整理的关于Python 可迭代对象、for循环、迭代器与生成器详解导语可迭代对象迭代器生成器总结的全部内容,更多相关Python内容请搜索靠谱客的其他文章。
发表评论 取消回复