python入门系列第十四篇: 迭代器

143 阅读3分钟
原文链接: zhuanlan.zhihu.com

基本概念

迭代器协议: 对象需要提供next方法,它要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代。

可迭代对象: 实现了迭代器协议的对象。

协议: 是一种约定, 可迭代对象实现了迭代器协议, python的内置工具for,in成员关系测试, map函数等使用迭代器协议访问可迭代对象。


文件迭代器


>>> f = open('./test.py', 'r')
>>> f.readline()
"s1 = 'spam'\n"
>>> f.readline()
"s2 = 'scam'\n"
>>> f.readline()
'res = []\n'
>>> f.readline()
'print(res)\n'
>>> f.readline()
''
>>> f.readline()
''
>>>

我们可以通过readline读取文件内容的下一行, 直到到达文件末尾返回一个空字符串。

其实, 文件对象还有一个_next_方法, 也是读取文件下一个内容, 不过当到达文件末尾时候, __next__会触发StopIteration异常。


>>> f = open('./test.py', 'r')
>>> f.__next__()
"s1 = 'spam'\n"
>>> f.__next__()
"s2 = 'scam'\n"
>>> f.__next__()
'res = []\n'
>>> f.__next__()
'print(res)\n'
>>> f.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>

这其实指的就是python中的可迭代协议: 有__next__方法的对象会前进到下一个结果, 等到了末尾时候会触发StopIteration。 任何上述这类对象都是可迭代的。

这类对象可以使用for循环或者其他迭代工具进行遍历, 因为所有迭代工具内部都是每次调用__next__, 并捕捉StopIteration异常。

# for循环逐行读取 代码简单, 运行速度快, 内存使用合理
>>> for line in open('./test.py', 'r'): print(line, end=' ')
...
s1 = 'spam'
 s2 = 'scam'
 res = []
 print(res)
 >>>

或者使用readlines方法, 但是不推荐, 性能不好。

>>> for line in open('./test.py', 'r').readlines(): print(line, end=' ')
...
s1 = 'spam'
 s2 = 'scam'
 res = []
 print(res)
 >>>

当然也支持使用while循环

f = open('./test.py', 'r')
while True:
    line = f.readline()
    if not line: break
    print(line, end=' ')
手动迭代: iter, next

python3提供了一个内置函数next(), 它会自动调用_next_方法, 给定一个迭代对象x,调用next(x)等同于x.__next__()。

>>> f = open('./test.py', 'r')
>>> next(f)
"s1 = 'spam'\n"
>>> next(f)
"s2 = 'scam'\n"
>>> next(f)
'res = []\n'
>>> next(f)
'print(res)\n'
>>> next(f)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>


iter内置函数会从可迭代对象中获得一个迭代器,该迭代对象含有next()。

>>> L = [1, 2, 3]
>>> L.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'next'
>>> next(L)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'list' object is not an iterator
>>> I = iter(L)
>>> next(I)
1
>>> next(I)
2
>>> next(I)
3
>>> next(I)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>


其他内置类型迭代器
# 遍历字典
>>> D = {'name': 'kobe', 'age': 20}
>>> for x in D.keys(): print(x, '=>', D[x])
...
name => kobe
age => 20
>>>

其实字典也有一个迭代器

>>> D
{'name': 'kobe', 'age': 20}
>>> I = iter(D)
>>> next(I)
'name'
>>> next(I)
'age'
>>> next(I)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>

或者直接使用for循环。

>>> for key in D: print(key, '=>', D[key])
...
name => kobe
age => 20
>>>


列表解析
# 一个基本的列表解析
>>> L = [x + 10 for x in range(10)]
>>> L
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>>

列表解析运行速度快于for循环, 这是因为它们的迭代在解释器内部是以C语言速度执行的。

当我们考虑在一个序列中对每一项执行一个操作时候,我们其实就可以考虑使用列表解析。