Python interview - Iterators & Generators

Iterator 迭代器 和 Generator 生成器


1. Iterators - python中的所有容器都是可以迭代的,for语句会调用容器对象中的__iter__()方法。该方法会返回一个定义了的next()方法的迭代器,在容器中逐一访问元素,当没有后续元素时候,抛出StopIteration异常。

用for loop 来对list进行操作

for i in [1,2,3,4]:
    print i

1
2
3
4

用for loop来对string进行操作

for c in 'python':
    print c

p
y
t
g
o
n

用for loop来对dictionary进行操作

for k in {'x':1, 'y':2}:
    print k

y
x

用for loop来对文件进行操作

for line in open('a.txt'):
    print line

first line
second line

称上述对象为可迭代对象。


有iter方法对可迭代对象进行操作,并且返回一个迭代器。

x = iter([1,2])

print x
<listiterator object at 0x0000000001DA29E8>

print x.next()
1

print x.next()
2

print x.next()
    print x.next()
StopIteration

每次都可以使用next()方法访问下一个元素,如果不存在下一个元素,那么就会raise一个StopIteration的异常。


迭代器的实现

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()


__iter__方法,就是使一个对象可迭代。此外,还有next方法,并且handle住找不到元素,raise一个StopIteration。

>>> y = yrange(3)
>>> y.next()
0
>>> y.next()
1
>>> y.next()
2
>>> y.next()
Traceback (most recent call last):
StopIteration


list,sum等方法都能接收迭代器作为一个参数传入。

>>> list(yrange(5))
[0, 1, 2, 3, 4]
>>> sum(yrange(5))
10

有些不同的是下面的例子,对于 iterable和iterator用文字理解有点描述能力不足,就看例子解释。

class zrange:
    def __init__(self, n):
        self.n = n

    def __iter__(self):
        return zrange_iter(self.n)

class zrange_iter:
    def __init__(self, n):
        self.i = 0
        self.n = n

    def __iter__(self):
        # Iterators are iterables too.
        # Adding this functions to make them so.
        return self

    def next(self):
        if self.i < self.n:
            i = self.i
            self.i += 1
            return i
        else:
            raise StopIteration()

上述代码不同的是,zrange作为 iterable,zrange_iter作为iterator,相比较于yrange我们来测试cases。

>>> 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]

对于上述的结果,总结是当iterable和iterator是同一个对象的时候,这个就是一个single iteration,单次的迭代。

我觉得比较好理解的就是y和z都是同样的object,他们并没有变,变的是迭代方法里的next函数。其中的i进行了变化。对于y而言,第一次list方法操作,next已经不会再

继续取下一个元素,而对于z而言,本身就含有一个zrange_iter的返回对象,每次都会从i=0开始进行next取下一个元素。这个就是最基本的区别。


2. Generator

对于生成器generator来说,就是一个生产一系列结果而不是一个单一结果的方法。

def yrange(n):
    i = 0
    while i < n:
        yield i
        i += 1

这里要提一下yield关键词,作用类似return,返回当前的值,但是并且保存,下次访问的时候从之前的保存值开始继续。


y = yrange(3)

print y
<generator object yrange at 0x0000000001DF31B0>

print y.next()
1

print y.next()
2

print y.next()
    print y.next()
StopIteration

所以可以见得,一个generator也是一个迭代器iterator。

当一个构造器方法被调用的时候,先返回一个构造器对象,但是并没有执行方法。一直到next方法被调用,才执行,并且一直遇到yield关键词的时候,返回给next方法值。


def foo():
    print "begin"
    for i in range(3):
        print "before yield", i
        yield i
        print "after yield", i
    print "end"

f = foo()
f.next()
# begin
# before yield 0
0
f.next()
# after yield 0
# before yield 1
1
f.next()
# after yield 1
# before yield 2
2
f.next()
# after yield 2
# end
# Traceback (most recent call last):
# StopIteration

补充例子,Fibonacci数列。

def fib(max):
    a, b = 1, 1
    while a < max:
        yield a #generators return an iterator that returns a stream of values.
        a, b = b, a+b


每次调用,运行到yield就会返回值,并且所有状态都会被保持住,一直到下个循环,或者碰到异常。

第一次碰到yield,返回a=1,继续a,b=1,2.

第二次,返回a=1,继续a,b=2,3

第三次,返回a=2,继续a,b=3,5

。。

最终得到Fib:1,1,2,3,5,8,13


生成器是迭代器的一种,通过生成器可以免去写迭代器的繁琐代码。迭代方法如下:

class Fib:
    def __init__(self, max):
        self.max = max
    def __iter__(self):
        self.a = 0
        self.b = 1
        return self
    def next(self):
        fib = self.a
        if fib > self.max:
            raise StopIteration
        self.a, self.b = self.b, self.a + self.b
        return fib



3. Generator Expression


Generator Expression就是generator版本的list comprehensions。看上去像是list comprehensions,但是返回的是一个generator,而不是一个list。

用list comprehensions的话

l = [x*2 for x in range(5)]
print l
# [0,2,4,6,8]
print sum(l)
# 20

而如果用的是Generator Expression,就仅仅是把中括号换成小括号。你会得到如下:
l = (x*2 for x in range(5))
print l
# <generator object <genexpr> at 0x0000000001E031B0>
print sum(l)
# 20

在这里你可以看到,l是一个generator对象,并不是一个list。所以说Generator Expression不仅减少了内存的开销,还提供了iterator的functionality,我们可以通过next方法来获得generator生成的item,比如:

print l.next()
# 0
print l.next()
# 2

但是要注意的是,Generator Expression得到的generator都仅仅是提供了一个 single iteration,单次迭代。如果在sum方法之后再调用next,就会抛出异常。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值