代码改变世界

学习7: 列表生成式,生成器,迭代器,可迭代对象

2017-02-08 10:17  一方书斋  阅读(291)  评论(0编辑  收藏  举报

1) 列表生成式,即创建列表的方式

列表生成式,这里是中括号[]

>>> [x*x for x in range(0,10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]
>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
>>> 

2) 生成器(Generator)
生成器有两种方法:第一个是通过生成器表达式来创建,另外一种是通过定义带有yield的函数来实现。

生成器, 这里是小括号()

>>> (x*x for x in range(0,10))
<generator object <genexpr> at 0x0000000000C15240>
>>> 

说明:从这个可以看出生成器和列表生成的区别:列表生成式直接返回了表达式的结果列表, 而生成器是一个对象,该对象包含了对表达式结果的计算引用。
前者如果数据量太大时候会占用很大内存,而生成器保存的只是一个计算对象,不会占用很大内存。

如果上述算法比较复杂,不好写在一个(),那么可以使用定义一个包含yield的函数来实现,这个就是第二种实现方法。
如果一个函数定义中包含了yield关键字,这个函数就不再是普通函数,而是一个generator object。
要想调用这个函数,需要使用next()函数,并且遇到yield语句返回(可以把yield理解为return)。例如:

def test():
    print "one"
    yield 
    print "two"
    yield 
    print 'three'
    yield
t=test()
t.next()
t.next()

输出结果为:
one
two
[Finished in 0.2s]

说明:首先创建生成器对象实例t,然后通过next方法调用函数,每一次执行到yield语句会被终止,并在下次该实例再次执行next方法时候继续执行。所以第一个next方法打印one,第二个next打印two.
test().next()
test().next()
输出结果为:
one
one
[Finished in 0.2s]
说明:注意必须是同一个实例不断调用next方法,如上这种调用方法达不到想要的效果。

def test():
    print "one"
    yield 
    print "two"
    return
    print "testing"
    yield 
    print 'three'
    yield
t= test()
t.next()
t.next()

说明:如果生成器中有return,在执行过程中 return,则直接抛出 StopIteration 终止迭代。

 

def readfile(file_name):
    seek_option=0
    while True:
        with open(file_name) as f:
            f.seek(seek_option)
            data = f.readline()
            if data:
                yield data
                seek_option = f.tell()
            else:
                return
print readfile('README.md')
file = readfile('README.md')
for data in file:
    print data
for data in readfile('README.md'):
    print data

说明: 上述定义了一个读取文件的生成器。生成器一般可以和for循环配合使用,这样可以迭代生成器元素,而不使用next方法。

 

3)迭代器(Iterators)
任何具有__next__()方法的对象都是迭代器。
所以生成器是一种特殊的迭代器,任意一个生成器都属于迭代器。

 

4)可迭代对象
可迭代对象可以为任意对象,不一定非得是基本数据结构,只要这个对象可以返回一个iterator。
可迭代对象通过iter函数可以转化为迭代器,如下例a是一个可迭代对象,而b是一个迭代器。
实际在for循环的过程中也都是将可迭代对象首先转化成迭代器,然后通过next方法读取元素直到抛出异常。

>>> a=[1,2,3,4]
>>> b=iter(a)
>>> type(a)
<type 'list'>
>>> type(b)
<type 'listiterator'>
>>> b.next()
1
>>> b.next()
2
>>>