Python编程中实现迭代器的一些技巧小结

yipeiwu_com5年前Python基础

yield实现迭代器
如引言中的描述,实现一个可迭代的功能要是每次都手动实现iter,next稍稍有点麻烦,所需的代码也是比较客观。在python中也能通过借助yield的方式来实现一个迭代器。yield有一个关键的作能,它能够中断当前的执行逻辑,保持住现场(各种值的状态,执行的位置等等),返回相应的值,下一次执行的时候能够无缝的接着上次的地方继续执行,如此循环反复知道满足事先设置的退出条件或者发生错误强制被中断。
其具体功能是可以当return使用,从函数里返回一个值,不同之处是用yield返回之后,可以让函数从上回yield返回的地点继续执行。也就是说,yield返回函数,交给调用者一个返回值,然后再“瞬移”回去,让函数继续运行, 直到吓一跳yield语句再返回一个新的值。使用yield返回后,调用者实际得到的是一个迭代器对象,迭代器的值就是返回值,而调用该迭代器的next()方法会导致该函数恢复yield语句的执行环境继续往下跑,直到遇到下一个yield为止,如果遇不到yield,就会抛出异常表示迭代结束。
看一个例子:

>>> def test_yield():
... yield 1
... yield 2
... yield (1,2)
...
>>> a = test_yield()
>>> a.next()
1
>>> a.next()
2
>>> a.next()
(1, 2)
>>> a.next()
Traceback (most recent call last):
 File "<stdin>", line 1, in ?
StopIteration

光听描述就觉得和迭代器的工作方式很一致是吧,的确,yield能把它所在的函索变成一个迭代器,拿最经典的菲波那切数列的例子聊简述一下工作的方式:

def fab(max): 
 n, a, b = 0, 0, 1 
 while n < max:
 print b, "is generated" 
 yield b
 a, b = b, a + b 
 n = n + 1 

>>> for item in fab(5):
... print item
... 
1 is generated
1
1 is generated
1
2 is generated
2
3 is generated
3
5 is generated
5

我们有回想一下for关键字的语法糖,在这里遍历5以内的菲波那切数列值的时候,很显然fab(5)生成了一个可迭代的对象,遍历开始的时候它的iter方法被调用返回一个实际工作的迭代器对象,然后每一次调用它的next方法返回一个菲波那切数列值然后打印出来。
我们可以将调用生成器函数返回的对象的属性打印出来,看一下到底发生了什么:

>>> temp_gen = fab(5)
>>> dir(temp_gen)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__iter__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'next', 'send', 'throw']

正如上面的描述,单纯调用fab并不会让函数立刻开始返回任何值,并且从打印出的fab(5)的属性列表能够看到,生成器函数返回的对象包含有__iter__,next的实现。与我们手动实现相比,使用yield很方便的就能够实现我们想要的功能,代码量缩减不少。

Generator Expression
python中另一种能更优雅生成迭代器对象的方式就是使用生成器表达式Generator expression,它和列表解析表达式有着非常相似的写法,仅仅是把中括号[]变成()而已,不过小小改变产生的实际效果确实大大的不一样:

>>> temp_gen = (x for x in range(5))
>>> temp_gen
<generator object <genexpr> at 0x7192d8>
>>> for item in temp_gen:
... print item
... 
0
1
2
3
4
>>> dir(temp_gen)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__iter__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'next', 'send', 'throw']

看过上面对yield的描述,这个例子以及对应的输出日志还是相当直接明了的,无论是temp_gen的打印日志描述,for语句遍历的输出结果还是调用dir输出的属性列表,都赤裸裸的表明生成器表达式确实生成了能够支持迭代的对象。另外表达式里面也能够调用函数,增加适量的过滤条件。

内置库itertools 和 iter
python内置的库itertools提供了大量的工具方法,这些方法能够帮助我们创建能进行高效遍历和迭代的对象,里面包含不少有意思并且有用的方法,比如像chain, izip/izip_longest, combinations, ifilter等等。在python中还有一个内置的iter函数非常有用,能够返回一个迭代器对象,之后也就能够进行可以查看对应的帮助文档简单看一下:

>>> iter('abc')
<iterator object at 0x718590>
>>> str_iterator = iter('abc')
>>> next(str_iterator)
'a'
>>> next(str_iterator)
'b'
>>> lst_gen = iter([1,2,3,4])
>>> lst_gen
<listiterator object at 0x728e30>
>>> dir(lst_gen)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__iter__', '__length_hint__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'next']

>>> help(iter)
Help on built-in function iter in module builtins:

iter(...)
 iter(iterable) -> iterator
 iter(callable, sentinel) -> iterator

 Get an iterator from an object. In the first form, the argument must
 supply its own iterator, or be a sequence.
 In the second form, the callable is called until it returns the sentinel.

相关文章

python根据距离和时长计算配速示例

复制代码 代码如下:function cal_pace(d,h,m,s){ var distance = d; var hours = h; var min...

python判断字符串是否纯数字的方法

本文实例讲述了python判断字符串是否纯数字的方法。分享给大家供大家参考。具体如下: 判断的代码如下,通过异常判断不能区分前面带正负号的区别,正则表达式可以根据自己需要比较灵活的写,通...

python中用logging实现日志滚动和过期日志删除功能

logging库提供了两个可以用于日志滚动的class(可以参考https://docs.python.org/2/library/logging.handlers.html),一个是R...

Python 编码规范(Google Python Style Guide)

Python 风格规范(Google) 本项目并非 Google 官方项目, 而是由国内程序员凭热情创建和维护。 如果你关注的是 Google 官方英文版, 请移步 Googl...

python实现的守护进程(Daemon)用法实例

本文实例讲述了python实现的守护进程(Daemon)用法。分享给大家供大家参考。具体如下: def createDaemon(): "'Funzione che crea u...