python迭代器资料整理
什么是迭代器
相关概念定义
迭代器(Iterator):
满足迭代协议的对象就是迭代器
iterator就是实现了Iteration Protocol的对象,这类对象都支持循环遍历的操作(for/while/支持迭代的函数list() sum()…)。
迭代协议(Iteration Protocol):
内建函数iter()
接收一个可迭代对象,并返回一个可迭代对象.
每次将这个可迭代对象传递给next()
函数,都会返回它所包含的下一个元素,当迭代完最后一个元素时,就会触发StopIteration异常。
>>> x = iter([1, 2, 3]) >>> x <listiterator object at 0x1004ca850> >>> next(x) 1 >>> next(x) 2 >>> next(x) 3 >>> next(x) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
满足以上要求的对象,就是迭代器。
迭代隐含的操作
在每次的迭代语句中,python都会按照迭代协议去对迭代器进行迭代。其实,在实际执行中,python会进行一些其他的操作:
将需要迭代的对象作为参数传递给iter
函数
iter
返回一个迭代器对象
每次循环则将返回的迭代器对象传递给next
函数
循环至最后一个元素,触发StopIteration
以for语句为例:
当我们在Python中执行循环语句for i in foo
的时候,其背后的操作是:
foo = iter(foo)
next(foo)
next(foo)在python3中执行的是:foo.__next__()
,在python2中则是:foo.next()
迭代器的实现
迭代器是用class来实现的。其中必需实现的有两个方法:__iter__
、next
(python2)/__next__
(python3)。其中,__iter__
必需返回一个迭代器对象,next
则负责迭代逻辑并在迭代完毕时触发异常。
如下:
def Iter(object) def __init__(self): pass def __iter__(self): pass def __next__(self): # python3 pass def next(self): # python2 pass
迭代器的特殊情况
迭代器的__iter__
返回self
迭代器的__iter__
方法需要返回的是一个具有next
方法的可迭代对象。如果当__iter__
返回的是self
的话,就会产生其他意想不到的效果。
class yrange: def __init__(self, n): self.i = 0 self.n = n def __iter__(self): return self def next(self): if self.i < self.n: i = self.i self.i += 1 return i else: raise StopIteration() class zrange: def __init__(self, n): self.i = 0 self.n = n def __iter__(self): return zrange(self.n) def next(self): if self.i < self.n: i = self.i self.i += 1 return i else: raise StopIteration()
执行结果:
>>> y = yrange(5) >>> list(y) [0, 1, 2, 3, 4] >>> list(y) [] >>> z = zrange(5) >>> list(z) [0, 1, 2, 3, 4] >>> list(z) [0, 1, 2, 3, 4]
在yrange
中,iter
返回的是self
,在执行list(y)
时iter
返回的都是同一个self
,所以再次调用list(y)
时只会触发结束迭代异常,列表中并无内容。
而在zrange
中,每次执行list(z)
时,iter
都是返回一个新的迭代器zrange(self.n)
,所以每次执行list(z)
都得到完整的元素。
生成器的迭代
通常,对于数据量特别大的序列,我们会用生成器generator
来代替容器对象container
,这样可以利用lazy evaluable来节省内存开销。值得注意的是,生成器也是一个只能迭代一次的迭代器。
def grange(n): i = 0 while i < n: yield i i += 1
执行结果:
>>> glist = grange(10) >>> list(glist) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> list(glist) []
如果是利用便捷的生成器表达式也是一样:
>>> alist = (i for i in range(10)) >>> list(alist) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> list(alist) []
要解决这个问题,可以将迭代器和生成器组合使用:
class Grange(object): def __init__(self, n): self.n = n def __iter__(self): for i in range(self.n): yield i
结果:
>>> glist = Grange(10) >>> list(glist) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> list(glist) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
值得注意的是,平常我们利用到生成器的地方都是数据量特别大的情况,这个时候,其实应该尽量避免多次迭代生成器。我想这应该也是python没有支持对生成器多次迭代的特性的原因。
编程建议
在实际的编程中,往往需要在函数中多次迭代一个序列,如果这个序列是调用API得到的,而你又不能保证它是没有陷阱的迭代器时。可以在遍历迭代器的时候,加入一个判断语句,避免无法多次迭代的情况发生:
def iterator_checker(iterator): assert iter(iterator) is not iter(iterator), "iter() return self"
参考资料
python官网迭代器文档
python practice book
<<Effective Python>>
https://segmentfault.com/a/1190000005915893
相关推荐
-
【Python3】Python运算符 python基础
2019-4-27
-
python获取与读写数据 python基础
2019-8-28
-
python中使用ctypes调用so传参设置 python基础
2019-6-22
-
聊聊Python 3 的字符串:str 和 bytes 的区别 python基础
2019-5-3
-
Python面向对象之面向对象基本概念 python基础
2019-9-11
-
Logistic回归模型和Python实现 python基础
2019-5-20
-
浅析 Python 的类、继承和多态 python基础
2018-2-3
-
python 装饰器 python基础
2019-9-2
-
带你快速了解WSGI python基础
2019-8-28
-
Python与数据库的新人手册 — MySQL python基础
2019-1-25