天天看点

第八章 Python可迭代对象、迭代器和生成器

<b>8.1 可迭代对象(iterable)</b>

大部分对象都是可迭代,只要实现了__iter__方法的对象就是可迭代的。

__iter__方法会返回迭代器(iterator)本身,例如:

&gt;&gt;&gt; lst = [1,2,3]

&gt;&gt;&gt; lst.__iter__()

&lt;listiterator object at 0x7f97c549aa50&gt;

python提供一些语句和关键字用于访问可迭代对象的元素,比如for循环、列表解析、逻辑操作符等。

判断一个对象是否是可迭代对象:

&gt;&gt;&gt; from collections import iterable  # 只导入iterable方法

&gt;&gt;&gt; isinstance('abc', iterable)     

true

&gt;&gt;&gt; isinstance(1, iterable)     

false

&gt;&gt;&gt; isinstance([], iterable)

这里的isinstance()函数用于判断对象类型,后面会讲到。

可迭代对象一般都用for循环遍历元素,也就是能用for循环的对象都可称为可迭代对象。

例如,遍历列表:

&gt;&gt;&gt; lst = [1, 2, 3]

&gt;&gt;&gt; for i in lst:

...   print i

...

1

2

3

<b></b>

博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang

qq群:323779636(shell/python运维开发群)

8.2 迭代器(iterator)

具有next方法的对象都是迭代器。在调用next方法时,迭代器会返回它的下一个值。如果next方法被调用,但迭代器没有值可以返回,就会引发一个stopiteration异常。

使用迭代器的好处:

1)如果使用列表,计算值时会一次获取所有值,那么就会占用更多的内存。而迭代器则是一个接一个计算。

2)使代码更通用、更简单。

<b>   8.2.1 迭代器规则</b>

   回忆下在python数据类型章节讲解到字典迭代器方法,来举例说明下迭代器规则:

&gt;&gt;&gt; d = {'a':1, 'b':2, 'c':3}

&gt;&gt;&gt; d.iteritems()

&lt;dictionary-itemiterator object at 0x7f97c3b1bcb0&gt;

# 判断是否是迭代器

&gt;&gt;&gt; from collections import iterator

&gt;&gt;&gt; isinstance(d, iterator)

&gt;&gt;&gt; isinstance(d.iteritems(), iterator)

# 使用next方法。

&gt;&gt;&gt; iter_items = d.iteritems()

&gt;&gt;&gt; iter_items.next()

('a', 1)

('c', 3)

('b', 2)

由于字典是无序的,所以显示的是无序的,实际是按照顺序获取的下一个元素。

<b>   8.2.2 iter()函数</b>

   使用iter()函数转换成迭代器:

语法:

  iter(collection) -&gt; iterator

  iter(callable, sentinel) -&gt; iterator

&gt;&gt;&gt; lst = [1, 2, 3]

&gt;&gt;&gt; isinstance(lst, iterator)

&gt;&gt;&gt; lst.next()  # 不是迭代器是不具备next()属性的

traceback (most recent call last):

  file "&lt;stdin&gt;", line 1, in &lt;module&gt;

attributeerror: 'list' object has no attribute 'next'

&gt;&gt;&gt; iter_lst = iter(lst)             

&gt;&gt;&gt; isinstance(iter_lst, iterator)

&gt;&gt;&gt; iter_lst.next()

   <b>8.2.3 itertools模块</b>

itertools模块是python内建模块,提供可操作迭代对象的函数。可以生成迭代器,也可以生成无限的序列迭代器。

有下面几种生成无限序列的方法:

count([n]) --&gt; n, n+1, n+2, ...

cycle(p) --&gt; p0, p1, ... plast, p0, p1, ...

repeat(elem [,n]) --&gt; elem, elem, elem, ... endlessly or up to n times 

也有几个操作迭代器的方法:

   islice(seq, [start,] stop [, step]) --&gt; elements from

chain(p, q, ...) --&gt; p0, p1, ... plast, q0, q1, ...

groupby(iterable[, keyfunc]) --&gt; sub-iterators grouped by value of keyfunc(v) 

imap(fun, p, q, ...) --&gt; fun(p0, q0), fun(p1, q1), ...

ifilter(pred, seq) --&gt; elements of seq where pred(elem) is true

 1)count生成序列迭代器

&gt;&gt;&gt; from itertools import *  # 导入所有方法

      # 用法 count(start=0, step=1) --&gt; count object

&gt;&gt;&gt; counter = count()    

&gt;&gt;&gt; counter.next()

...... 

可以使用start参数设置开始值,step设置步长。

    2)cycle用可迭代对象生成迭代器

      # 用法 cycle(iterable) --&gt; cycle object

&gt;&gt;&gt; i = cycle(['a', 'b', 'c'])  

&gt;&gt;&gt; i.next()

'a'

'b'

'c'

   3)repeat用对象生成迭代器

# 用法 repeat(object [,times]) -&gt; create an iterator which returns the object,就是任意对象

&gt;&gt;&gt; i = repeat(1)

......

可使用无限次。

也可以指定次数:

     &gt;&gt;&gt; i = repeat(1, 2)

stopiteration

   4)islice用可迭代对象并设置结束位置

      # 用法 islice(iterable, [start,] stop [, step]) --&gt; islice object

&gt;&gt;&gt; i = islice([1,2,3],2)   

&gt;&gt;&gt; i.next()             

正常的话也可以获取的3。

   5)chain用多个可迭代对象生成迭代器

# 用法 chain(*iterables) --&gt; chain object

&gt;&gt;&gt; i = chain('a','b','c')

   6)groupby将可迭代对象中重复的元素挑出来放到一个迭代器中

# 用法 groupby(iterable[, keyfunc]) -&gt; create an iterator which returns

&gt;&gt;&gt; for key,group in groupby('abcddcca'):

...   print key,list(group)               

a ['a']

b ['b']

c ['c']

d ['d', 'd']

groupby方法是区分大小写的,如果想把大小写的都放到一个迭代器中,可以定义函数处理下:

&gt;&gt;&gt; for key,group in groupby('abcddcca', lambda c: c.upper()):

...   print key, list(group)

c ['c', 'c']

   7)imap用函数处理多个可迭代对象

# 用法 imap(func, *iterables) --&gt; imap object

&gt;&gt;&gt; a = imap(lambda x, y: x * y,[1,2,3],[4,5,6])   

&gt;&gt;&gt; a.next()

4

10

18

   8)ifilter过滤序列

# 用法 ifilter(function or none, sequence) --&gt; ifilter object

&gt;&gt;&gt; i = ifilter(lambda x: x%2==0,[1,2,3,4,5])

&gt;&gt;&gt; for i in i:

当使用for语句遍历迭代器时,步骤大致这样的,先调用迭代器对象的__iter__方法获取迭代器对象,再调用对象的__next__()方法获取下一个元素。最后引发stopiteration异常结束循环。

<b>8.3 生成器(generator)</b>

什么是生成器?

1)任何包含yield语句的函数都称为生成器。

2)生成器都是一个迭代器,但迭代器不一定是生成器。

<b>8.3.1 生成器函数</b>

在函数定义中使用yield语句就创建了一个生成器函数,而不是普通的函数。

当调用生成器函数时,每次执行到yield语句,生成器的状态将被冻结起来,并将结果返回__next__调用者。冻结意思是局部的状态都会被保存起来,包括局部变量绑定、指令指针。确保下一次调用时能从上一次的状态继续。

以生成斐波那契数列举例说明yield使用:

斐波那契(fibonacci)数列是一个简单的递归数列,任意一个数都可以由前两个数相加得到。

#!/usr/bin/python

# -*- coding: utf-8 -*-

def fab(max):

    n, a, b = 0, 0, 1

    while n &lt; max:

        print b

        a, b = b, a + b

        n += 1

fab(5)

# python test.py

5

使用yied语句,只需要把print b改成yield b即可:

        yield b

        # print b

print fab(5)

&lt;generator object fab at 0x7f2369495820&gt;

可见,调用fab函数不会执行fab函数,而是直接返回了一个生成器对象,上面说过生成器就是一个迭代器。那么就可以通过next方法来返回它下一个值。

&gt;&gt;&gt; import test

&gt;&gt;&gt; f = test.fab(5)   

&gt;&gt;&gt; f.next()       

&gt;&gt;&gt; f.next()                               

&gt;&gt;&gt; f.next()

每次fab函数的next方法,就会执行fab函数,执行到yield b时,fab函数返回一个值,下一次执行next方法时,代码从yield b的吓一跳语句继续执行,直到再遇到yield。

<b>8.3.2 生成器表达式</b>

在第四章 python运算符和流程控制章节讲过,简化for和if语句,使用小括号()返回一个生成器,中括号[]生成一个列表。

回顾下:

# 生成器表达式

&gt;&gt;&gt; result = (x for x in range(5))

&gt;&gt;&gt; result

&lt;generator object &lt;genexpr&gt; at 0x030a4fd0&gt;

&gt;&gt;&gt; type(result)

&lt;type 'generator'&gt;

# 列表解析表达式

&gt;&gt;&gt; result = [ x for x in range(5)]

&lt;type 'list'&gt;

[0, 1, 2, 3, 4]

第一个就是生成器表达式,返回的是一个生成器,就可以使用next方法,来获取下一个元素:

&gt;&gt;&gt; result.next()