我们经常会用到for循环、列表推导、以及很多内置函数如map、sorted等,其中的原理到底如何,方法调用模式又是什么呢,掌握这些对我们理解python非常重要,也方便理解别人的代码。
先浅显地理解for循环的方法调用过程,在for循环开始时,会首先把可迭代对象传入内部函数iter,iter函数调用可迭代对象的__iter__方法,返回一个迭代器对象;返回的迭代器对象具有__next__方法,被next函数调用,依次从迭代器中取出元素,并检测StopIteration异常作为循环结束。对于文件这种自身就是迭代器,第一步可有可无,文件有自己的__next__方法。
下面解释一下各种看起来很高端的概念。
迭代协议
该协议基于分别用在不同步骤的两个对象:
- 可迭代对象:迭代的被调对象,比如列表字典等,其__iter__方法被iter函数调用。
- 迭代器对象:可迭代对象的返回结果,在迭代过程中实际提供值的对象。它的__next__方法被next运行,并在结束时出发StopIteration异常。
对于列表之类的可迭代对象,由于自身不是迭代器,因此支持多次迭代,对这样的对象,需要执行上述两个步骤进行迭代(调用iter启动迭代)。
>>>l = [1,2,3]
>>>iter(l) == l
False
>>>l.__next__()
AttributeError:'list' object has no attribute '__next__'
>>>s = iter(l)
>>>s.__next__()
1
>>>next(s)
2
range可迭代对象
range函数产生一个可迭代对象,根据请求产生范围内的数字,而不是在内存中构建一个结果列表。如果需要一个实际的范围列表,就需要使用list(range(…))来强制获取。
>>>r = range(10)
>>>r
range(0,10)
>>>i = iter(r)
>>>next(i)
0
>>>next(i)
1
>>>list(range(10))
[0,1,2,3,4,5,6,7,8,9]
map、zip和filter可迭代对象
与range函数类似,内置函数map、zip和filter也转变成可迭代对象以节约内存空间,而不是在内存中一次性生成一个结果列表。不同的是,它们本身都是迭代器,遍历结果一遍之后,它们就用尽了。
>>>m = map(abs, (-1,0,1))
>>>next(m)
1
>>>next(m)
0
>>>next(m)
1
>>>next(m)
StopIteration
>>>for i in m: print(i)
>>>m = map(abs, (-1,0,1))
>>>for i in m: print(i)
1
0
1
>>>list(map(abs, (-1,0,1)))
[1,0,1]