迭代器
迭代工具:for, 列表解析,in成员关系测试,map内置函数等。
可迭代对象:可以在迭代工具环境中(例如,for)一次产生一个结果的对象,就可以看做是可迭代的。
可迭代的:支持iter的一个对象
迭代器:iter所返回的一个支持next(I)的对象
迭代协议:有__next__方法的对象会前进到下一个结果,而在一系列结果的末尾时,则会引发StopIteration。在Python中,任何这类对象都认为是可迭代的。任何这类对象也能以for循环或其他迭代工具遍历,因为所有迭代工具内部工作起来都是在每次迭代中调用__next__,并且捕捉StopIteration异常来确定何时离开。
文件迭代器:
for line in open('script.py'): #use file iterators to read by lines
print(line.upper(), end=' ') #Calls __next__, catches StopIteration
上例是读取文本文件的最佳放啊,原因有三:这是最简单的写法,运行最快,并且从内测使用情况来说也是最好的
for i in open(xxx).readlines方法将文件内容加载到内存,会占用很多内存,如果文件很大,计算机内存空间不够,甚至不能工作。
比起迭代器for循环,while运行更慢,因为迭代器在python中以C语言的速度运行的,而while则是通过python虚拟机运行python字节码的。
手动迭代:iter and next
python 提供一个内置函数next,它会自动调用一个对象的__next__方法。
f = open('script.py')
next(f)
文件对象就是自己的迭代器,列表以及很多其他的内置对象,不是自身的迭代器,因为它们支持多次打开迭代器。对这样的对象,我们必须调用iter来启动迭代:
>>> l1
[1, 2, 3, 4]
>>> iter(l1)is l1 #列表不是自身的迭代器
False
>>> l1.__next__() #列表没有__next__方法
Traceback (most recent call last):
File "<pyshell#71>", line 1, in <module>
l1.__next__()
AttributeError: 'list' object has no attribute '__next__'
>>> I = iter(l1) #调用iter启动迭代
>>> I.__next__()
1
>>> next(I) # important; next(I) same as I.__next__
2
>>> I.next() # attention, this is not support
Traceback (most recent call last):
File "<pyshell#75>", line 1, in <module>
I.next()
AttributeError: 'list_iterator' object has no attribute 'next'
>>> for x in l1: # automation iteration
print(x, end=' ') # obtains iter, calls __next__, catches exceptions
1 2 3 4
>>> I = iter(l1) # manual iteration
>>> while True:
try: # try statement catches exceptions
x = next(I) # or calls I.__next__()
except StopIteration:
break
print(x**2, end=' ')
1 4 9 16
列表解析:初探
[x**2 for x in L]
[line.strip() for line in open(xxx) if line[0] == 'p']
[x+y for x in 'abc' for y in 'xyz']
列表解析并不完全和for循环语句版本相同,因为它会产生一个新的列表对象。列表解析会比手动的for循环语句运行的更快(往往快一倍),因为他们的迭代在解释器内部以C语言的速度执行的,而不是以手动python代码执行的,特别是对于较大的数据集合,这是使用列表解析的一个主要的性能优点。
其他迭代环境
列表解析、in成员关系测试、map内置函数以及像sorted和zip调用这样的内置函数也都使用了迭代协议。
map类似于列表解析,但是它更有局限性,因为它需要一个函数而不是一个任意的表达式。
sorted排序可迭代对象中的各项,zip组合可迭代对象中的各项,enumerate根据相对位置来配对可迭代对象的项,filter选择一个函数为真的项,reduce针对可迭代对象中的成对的项运行一个函数。所有这些都接受一个可迭代的对象。Python3中,zip, enumerate和filter也像map一样返回一个可迭代对象。
>>> sorted(open('threenames.py'))
["a = 'dude'\n", "b = 'parrot'\n", "c = 'sketch'\n", 'print(a,b,c)\n']
>>> map(str.upper,open('threenames.py'))
<map object at 0x000001C1649BE588>
>>> list(map(str.upper,open('threenames.py')))
["A = 'DUDE'\n", "B = 'PARROT'\n", "C = 'SKETCH'\n", 'PRINT(A,B,C)\n']
>>> 'aaa' in open('threenames.py')
False
>>> 'sketch' in open('threenames.py')
False
>>> "c = 'sketch'\n" in open('threenames.py')
True
>>> list(zip(open('threenames.py'),open('threenames.py')))
[("a = 'dude'\n", "a = 'dude'\n"), ("b = 'parrot'\n", "b = 'parrot'\n"), ("c = 'sketch'\n", "c = 'sketch'\n"), ('print(a,b,c)\n', 'print(a,b,c)\n')]
>>> list(enumerate(open('threenames.py')))
[(0, "a = 'dude'\n"), (1, "b = 'parrot'\n"), (2, "c = 'sketch'\n"), (3, 'print(a,b,c)\n')]
>>> list(filter(bool,open('threenames.py')))
["a = 'dude'\n", "b = 'parrot'\n", "c = 'sketch'\n", 'print(a,b,c)\n']
>>> import functools, operator
>>> functools.reduce(operator.add, open('threenames.py'))
"a = 'dude'\nb = 'parrot'\nc = 'sketch'\nprint(a,b,c)\n"
>>>
如下示例中的所有根据接受任何可迭代对象作为一个参数,并且使用迭代协议来扫描它,但返回单个的结果:
>>> l1
[1, 2, 3, 4]
>>> sum(l1)
10
>>> max(l1)
4
>>> min(l1)
1
>>> l2 = ['spam','','ni']
>>> any(l2)
True
>>> all(l2)
False
集合解析,字典解析
l1 = [1,2,3,3]
{x for x in l1}
{ix: x for ix, x in enumerate(l1)}
{x for x in l1 if x > 1}
{ix: x for ix, x in enumerate(l1) if x == 3}
其他迭代器主题:(后面介绍)
使用yield语句,用户定义的函数可以转换为可迭代的生成器函数
当编写圆括号的时候,列表解析转变为可迭代的生成器表达式
用户定义的类通过__iter__或__getitem__运算符重载变得可迭代
Learning Python, Fourth Edition, by Mark Lutz.