Python 迭代器

可迭代对象

首先我们来看两段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
lst = ["aaa", "bbb", "ccc"]
for i in lst:
print(i)
"""
结果:
aaa
bbb
ccc
"""


for i in 123:
print(i)
# 报错 TypeError: 'int' object is not iterable int对象不是可迭代对象

可见,并不是所有的数据类型都可以去使用for循环进行遍历,这是因为并不是所有的数据类型都是可迭代的对象。

在我们已知的基本数据类型中,str, list,tuple,set,dict,range,文件操作的句柄f均为可迭代对象,所有的以上数据类型中都有一个函数__iter()__,所有包含了__iter__()函数的数据类型都是可迭代的数据类型 (Iterable)。

我们可以通过 dir() 函数来查看一个对象或数据类型中包含了哪些东西。 在打印结果中,如果能找到__iter__,那么这个类的对象就是一个可迭代对象。

1
2
3
4
5
6
lst = ["aaa", "bbb", "ccc"]
print(dir(lst))

"""
['__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']
"""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
lst = ["aaa", "bbb", "ccc"]
tup = (1, 2, 3)
dic = {"a":"aaa"}
se = {1,2,3}

print("__iter__" in dir(lst)) # True
print("__iter__" in dir(tup)) # True
print("__iter__" in dir(dic)) # True
print("__iter__" in dir(se)) # True
print("__iter__" in dir(123)) # False

# 也可以打印类中声明的⽅方法和函数
print(dir(str)) # True
print(dir(list)) # True
print(dir(dict)) # True

迭代器

  • 迭代器是一个可迭代的对象,但是可迭代的对象不一定就是迭代器
  • 可迭代对象可以通过 __iter()__ 方法获取自身的迭代器
  • 迭代器可以通过 __next()__ 方法取值
1
2
3
4
5
6
lst = ["aaa", "bbb", "ccc"]
it = lst.__iter__() # it就是列表对象的迭代器
print(it.__next__()) # 取第一个值 aaa
print(it.__next__()) # 取第二个值 bbb
print(it.__next__()) # 取第三个值 ccc
print(it.__next__()) # 发生越界,停止迭代 StopIteration

迭代器使用next方法从上往下取值,只有获取到next方法才会返回值。如果取值过程出现中断,再次获取到next的时候,迭代器还是会接着上一次next的位置继续往下取值,不会从头重新开始。

1
2
3
4
5
6
7
lst = ["aaa", "bbb", "ccc"]
it = lst.__iter__() # it就是列表对象的迭代器
print(it.__next__()) # aaa
print("hahaha")
print("hahaha")
print("hahaha")
print(it.__next__()) # bbb

迭代器判断

首先,迭代器本身就是一个可迭代的对象,因此迭代器也有 __iter()__方法。通过 迭代器. __iter()__ 获取到的迭代器的迭代器其实就是它自己。其次,迭代器可以通过__next()__取值,而可迭代对象不一定有该方法。

1
2
3
4
5
6
7
8
9
10
11
print("__iter__" in dir(list))  # True
print("__next__" in dir(list)) # False

lst = ["aaa", "bbb", "ccc"]
it = list.__iter__(lst)
print("__iter__" in dir(it)) # True
print("__next__" in dir(it)) # True

f = open("aaa", mode="r", encoding="utf-8")
print("__iter__" in dir(f)) # True
print("__next__" in dir(f)) # True

列表对象只有 __iter__而没有__next__,因此列表只是一个可迭代对象而不是迭代器;列表对象的迭代器既有 __iter__又有__next__;

文件操作句柄既有 __iter__又有__next__,因此文件操作句柄f是一个迭代器。

我们还可以通过其他方式来判断可迭代对象和迭代器

1
2
3
4
5
6
7
8
9
10
# isinstance 函数用于判断某一个对象是否是某一个类的实例对象
l = [1,2,3]
l_iter = l.__iter__()

from collections import Iterable
from collections import Iterator
print(isinstance(l,Iterable)) #True 是一个可迭代的对象
print(isinstance(l,Iterator)) #False 不是一个迭代器
print(isinstance(l_iter,Iterator)) #True 是一个可迭代的对象
print(isinstance(l_iter,Iterable)) #True 是一个迭代器

注:列表、字典、元组、字符串、range 都是可迭代对象,文件句柄是迭代器。

使用迭代器实现for循环

1
2
3
4
5
6
7
lst = ["aaa", "bbb", "ccc", "ddd"]
it = lst.__iter__()
while True:
try:
print(it.__next__())
except StopIteration:
break

迭代器的特点

  • 节省内存。
  • 惰性机制,必须接收到next才会返回值。
  • 不能反复,只能向下执⾏。

Python 迭代器
https://clark-cdc.github.io/2019/04/12/0008-迭代器/
作者
clark
发布于
2019年4月12日
许可协议