容器和iterator
在Python中,可以对容器类(container,如list,tuple,set,dict等)进行遍历(iterate),这是因为这类容器实现了__iter__方法,该方法返回了一个iterator类型(iterator type)的对象。以字典类型为例:
x = {}
iter_over_x = x.__iter__() # 或者是调用iter(x)
type(iter_over_x) # dictionary-keyiterator,in python2.7
生成iterator type对象的方法有三种:
- 构造实现iterator protocol的类并实例化
- 调用generator expression
- 调用generator函数
iterator protocol
看一个对象是不是iterator type,要看该对象是否实现了以下两个方法,也就是通常所说的是否实现了 iterator protocol
-
__iter__
该方法通常返回自身,容器类也实现了这个方法,所以无论是容器类还是iterator类,都可以出现在 for item in object 语句中。 -
next
在python3中,被改名为__next__方法,调用该方法返回遍历的下一个元素,如果当前元素已经是最后一个元素,则抛出 StopIteration 异常。
一个iterator只能被遍历一次,而且遍历过程中不能回退到已经遍历的节点,而一个容器每次调用__iter__都会生成一个全新的iterator。
一个容器类也可以是一个iterator类型,这就要求其__iter__方法返回自身,而且实现next方法。如文件对象就是如此:
f = open('somefile')
iter_over_f = f.__iter__()
print f is iter_over_f # True
for循环 for 循环对用户封装了iterator 对象的生成和遍历,如
for item in container:
# process item
相比
_iter = iter(container)
while True:
try:
item = _iter.next()
except StopIteration:
break
# process item
明显简洁了很多。
iter的一个技巧
除了 iter(collection)这种方式外,iter还可以 iter(callable, sentinel)这么调用,直到callable返回sentinel时,停止对callable的调用
# iter(callable, sentinel),
# the callable is called until it returns the sentinel
CHUNKSIZE = 8192
def reader(s):
while True:
data = s.recv(CHUNKSIZE)
if data == b'':
break
#process_data(data)
def reader2(s):
for chunk in iter(lambda: s.recv(CHUNKSIZE), b''):
pass
#process_data(chunk)
generator expression
generator expression 返回的结果是generator对象,该对象实现了iterator protocol。
generator
generator function是指有yield语句的函数,调用该函数,不会执行该函数,返回的结果也不是某一个单一的值,而是一个generator对象,该对象实现了iterator protocol,当遍历该generator对象时,函数体内的逻辑才开始真正执行。
def x():
yield 1
_gen = x()
type(_gen) # generator