1、迭代
通过重复执行的代码处理相似的数据集的过程,并且本次迭代的处理数据要依赖上一次的结果继续往下做,上一次产生的结果为下一次产生结果的初始状态,如果中途有任何停顿,都不能算是迭代。
# 非迭代例子
n = 0
while n < 3:
print('hello world')
n += 1
hello world
hello world
hello world
# 迭代例子
n = 0
while n < 3:
print('第%s次 hello world'%(n+1))
n += 1
第1次 hello world
第2次 hello world
第3次 hello world
前者循环内3次输出" Hello world",输出的数据不依赖上一次的数据,因此不是跌代。而后者的循环内打印的3次数据都上一次循环的影响,所以是迭代。
2、容器 container
容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个地迭代获取,可以用 in, not in关键字判断元素是否包含在容器中。
-
从字面上理解,容器基本上可以包含其他类型对象(如列表、元组、字典等)作为元素的对象;在 Python 常见的数据类型中几乎所有的数据类型(字符串除外)都能包含其它类型的对象;
-
容器仅仅只是用来存放数据的,我们平常看到的 l = [1,2,3,4] 等等,表面上我们可以直接从列表容器中取出元素,但事实上容器并不提供这种能力,而是可迭代对象赋予了容器这种能力。
3、可迭代对象(Iterable)
3.1 基本概念
可迭代对象并不是指某种具体的数据类型,它是指存储了元素的一个容器对象,且容器中的元素可以通过__iter__( )
方法或__getitem__( )
方法访问。
-
Python实现一个通用的外部可以访问可迭代对象内部数据的接口:
__iter__()
方法的作用是让对象可以用for ... in
循环遍历__getitem__( )
方法是让对象可以通过“实例名[index/key]”的方式访问实例中的元素
-
一个可迭代对象是不能独立进行迭代的,Python中,迭代是通过
for ... in
来完成的。凡是可迭代对象都可以直接用for...in
循环访问,这个语句其实做了两件事:第一件事是调用__iter__()
获得一个可迭代器,第二件事是循环调用__next__()
。
3.2 判断一个对象是不是可迭代对象
可以使用 isinstance()
判断一个对象是否是 Iterable 对象
from collections import Iterable
class MyDataType1:
# 自定义的数据类型1
def __iter__(self):
pass
class MyDataType2:
# 自定义的数据类型2
pass
# 自定义的数据类型实例化
mydatatype1 = MyDataType1()
mydatatype2 = MyDataType2()
data_info = {"字典":{}, "列表":[], "字符串":'abc',
"元祖":(1,),"集合":{1,},"MyDataType1": mydatatype1, "MyDataType2": mydatatype2,
"数字":100,"boolen":True, "None":None}
for key,value in data_info.items():
result = isinstance(value, Iterable)
if result:
print(f"{key} is Iterable")
else:
print(f"{key} is not Iterable")
字典 is Iterable
列表 is Iterable
字符串 is Iterable
元祖 is Iterable
集合 is Iterable
MyDataType1 is Iterable
MyDataType2 is not Iterable
数字 is not Iterable
boolen is not Iterable
None is not Iterable
结合上述代码发现,添加了__iter__
方法的 MyDataType1 对象已经是一个可迭代对象啦!!!
3.3 __iter__()
函数与next()
函数
- iter 函数是让对象可以用
for ... in
循环遍历,即将对象转变为迭代器。 - next 函数要和iter() 函数一起使用,不断使用next()函数来获取下一条数据(for 循环实现的原因)。
# 没有 iter 时,next 能使用
li = [11, 22, 33, 44, 55]
next(li)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-4-b5d17e146774> in <module>
1 # 没有 iter 时,next 能使用
2 li = [11, 22, 33, 44, 55]
----> 3 next(li)
TypeError: 'list' object is not an iterator
li = [11, 22, 33, 44, 55]
li = iter(li) # iter()函数实际上就是调用了可迭 代对象的 __iter__ 方法。
next(li)
11
4、迭代器(Iterator)
4.1 基本概念
迭代器可以看作是一个特殊的对象,每次调用该对象时会返回自身的下一个元素,从实现上来看,一个迭代器对象必须是定义了__iter__()
方法和next()
方法的对象。
- Python 的 Iterator对象 表示的是一个数据流,可以把这个数据流看做是一个有序序列,但不能提前知道序列的长度,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算;
- Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误;
- 所有的 Iterable 可迭代对象均可以通过内置函数
iter()
来转变为迭代器 Iterator。 - 优点:节约内存(循环过程中,数据不用一次读入,在处理文件对象时特别有用,因为文件也是迭代器对象)、不依赖索引取值、实现惰性计算(需要时再取值计算);
- 缺点: 迭代器使用上存在限制:只能向前一个个地访问数据,已访问数据无法再次访问、遍历访问一次后再访问无数据
# 不能提前知道迭代器的长度
li = iter([11, 22])
print(len(li))
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-6-0f3ba65b9307> in <module>
1 # 不能提前知道迭代器的长度
2 li = iter([11, 22])
----> 3 print(len(li))
TypeError: object of type 'list_iterator' has no len()
# next 访问直至出现 StopIteration
li = iter([11, 22])
print(next(li))
print(next(li))
print(next(li))
11
22
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-7-3d729d10b8b7> in <module>
3 print(next(li))
4 print(next(li))
----> 5 print(next(li))
StopIteration:
# for 循环访问
li = iter([11, 22])
for i in li: print(i)
11
22
4.2 如何判断一个对象是否是迭代器
可以使用 isinstance() 判断一个对象是否是 Iterator 对象
from collections import Iterator
for key,value in data_info.items():
result = isinstance(value, Iterable)
if result:
print(f"{key} is Iterable ", end=' ')
else:
print(f"{key} is not Iterable ", end='')
if isinstance(value, Iterator):
print(f"is Iterator")
else:
print(f"is not Iterator")
字典 is Iterable is not Iterator
列表 is Iterable is not Iterator
字符串 is Iterable is not Iterator
元祖 is Iterable is not Iterator
集合 is Iterable is not Iterator
MyDataType1 is Iterable is not Iterator
MyDataType2 is not Iterable is not Iterator
数字 is not Iterable is not Iterator
boolen is not Iterable is not Iterator
None is not Iterable is not Iterator
class MyIterator:
def __iter__(self):
pass
def __next__(self):
pass
myiterator = MyIterator()
print(isinstance(myiterator, Iterable))
print(isinstance(myiterator, Iterator))
True
True
我们发现:
- 一个实现了
__iter__()
方法和__next__()
方法的对象,才是迭代器。 - 一个对象是可迭代对象,但是不一定是迭代器;一个迭代器对象一定是可迭代对象。
5、生成器 (generator)
在 Python 中,使用了 yield
的函数被称为生成器。
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。
在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。
调用一个生成器函数,返回的是一个迭代器对象。
5.1 创建生成器的方式1:
# 第一种方法很简单,只要把一个列表生成式的 [ ] 改成 ( )
L = [ x*2 for x in range(5)]
print(L)
G = ( x*2 for x in range(5))
print(G)
[0, 2, 4, 6, 8]
<generator object <genexpr> at 0x10c02fb48>
创建 L 和 G 的区别仅在于最外层的 [ ] 和 ( ) , L 是一个列表,而 G 是一个生成器。
我们可以直接 打印出列表L的每一个元素,而对于生成器G,我们可以按照迭代器的使用方法来使用,即可以通 过next()函数、for循环、list()等方法使用。
5.2 创建生成器的方式2:
generator非常强大。如果推算的算法比较复杂,用类似列表生成式的 for 循环无法实现的时候,还可以用函数来实现。
def fibonacci(n): # 生成器函数 - 斐波那契
a, b, counter = 0, 1, 0
while True:
if (counter > n):
return
yield a
a, b = b, a + b
counter += 1
f = fibonacci(10) # f 是一个迭代器,由生成器返回生
f
<generator object fibonacci at 0x10c02f9e8>
5.3 访问生成器数据
# list 等方法
print("第1种方式访问:")
print(list(fibonacci(10)))
# next 函数
print("第2种方式访问:")
f = fibonacci(10)
while True:
try:
print(next(f), end=" ")
except StopIteration:
print()
break
# for 循环
print("第3种方式访问:")
f = fibonacci(10)
for i in f:
print(f"{i} ", end="")
第1种方式访问:
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
第2种方式访问:
0 1 1 2 3 5 8 13 21 34 55
第3种方式访问:
0 1 1 2 3 5 8 13 21 34 55
- 使用了yield关键字的函数不再是函数,而是生成器。(使用了yield的函数就是生成器)
- yield关键字有两点作用:
- 保存当前运行状态(断点),然后暂停执行,即将生成器(函数)挂起
- 将yield关键字后面表达式的值作为返回值返回,此时可以理解为起到了return的作用
- 可以使用next()函数让生成器从断点处继续执行,即唤醒生成器(函数)
5.4 生成器与生成器函数的判断区分
使用 isgeneratorfunction 判断一个函数是不是生成器函数;
使用 isinstance 和 types.GeneratorType 判断一个对象是不是生成器对象
from inspect import isgeneratorfunction
import types
def f(x):
# 生成器函数
yield x
# 生成器对象
G = (x*2 for x in range(5))
# 生成器函数的判断
print("f 函数是生成器函数?:", isgeneratorfunction(f))
print("f(5) 是生成器函数?:", isgeneratorfunction(f(5)))
# 生成器对象的判断
print("f(5) 是生成器对象?:", isinstance(f(5), types.GeneratorType))
print(" G 是生成器对象?:", isinstance(G, types.GeneratorType))
f 函数是生成器函数?: True
f(5) 是生成器函数?: False
f(5) 是生成器对象?: True
G 是生成器对象?: True
6、最后用一张图来表达他们之间的关系
参考资料:
- https://blog.csdn.net/LaoYuanPython/article/details/89609187
- https://www.cnblogs.com/Aiyuqianer/p/14091062.html
- https://www.runoob.com/python3/python3-iterator-generator.html
- https://zhuanlan.zhihu.com/p/341439647