迭代器和简单的生成器

摘自: https://www.ibm.com/developerworks/cn/linux/sdk/python/charm-20/

python2.2引进了一种带有新型关键字的新型构造。这个构造是生成器;关键字是yield.生成器使几个新型,强大和富有表现力的编程习惯成为可能,但初看,要理解生成器,还是有一点难度。本文由浅入深的介绍了生成器,同时还介绍了迭代器的相关问题。

由于迭代器比较容易理解,让我们先看它。基本上迭代器是含有.next方法的对象。这样定义不十分正确但非常接近。事实上,当迭代器应用新的iter()内置函数的时候,大多数迭代器的上下文希望返回迭代器,需要使__iter__()方法返回self.本文的示例将会说明清楚这一点。如果迭代有一个逻辑的终止,则迭代器的.next()方法可能决定抛出StopIteration异常。
生成器要稍微复杂化和一般化一点。但生成器典型的用途是用来定义迭代器;所以不值得总是为一些细微之处而担心,生成器是这样一个函数它记住上一次返回时在函数体中的位置,对于生成器函数的第二次调用跳转到该函数的中间,而上次调用的所有的局部变量都被记住

随机遍历

让我们考虑一个简单的问题,可以使用很多方法解决它。假设我们想要一串正的随机数字流,这个数字流的要求是每一个数字不允许小于0.1,且相邻两个数字之间的大小相差绝对值不小于0.4

版本一

版本一是比较传统的做法,我们直接写一个随机产生一串数字流的函数,并将他保存在一个list中返回:

def generate_random():
    last, rand = 1, random.random()
    num = []
    while rand > 0.1:
        if abs(last - rand) >= 0.4:
            last = rand
            num.append(rand)
        else:
            print "invalid"
            rand = random.random()
    num.append(rand)
    return num

for item in generate_random():
    print item

从上面可以产出,这种方法有很大的局限性。首先这个示例中不可能产生庞大的数字列表,通过对数字的大小限制进行终结随机数的产生,我们可以通过限制进行预测此数列的大小。另一方面,在内存和性能方面使这种方法变得不切实际,以及没有必要。同样是这个问题,使得python较早的版本中添加了xrange()和xreadlines()。重要的是,许多流取决于外部事件,并且当每个元素可用时,才处理这些流。
在python2.1和较早的版本中,我们的诀窍是使用静态函数局部变量来记住函数上一次调用的事情。显而易见,全局变量可以用来做同样的工作,但是它会带来大家熟知的问题:命名空间污染问题
###版本二 使用静态成员解决上述问题

def generate_random_static(last = [1]):
    rand = random.random()
    if rand < 0.1:
        return
    while abs(rand - last[0]) < 0.4:
        print "invalid"
        rand = random.random()
    last[0] = rand
    return rand

num = generate_random_static()
while num is not None:
    print num
    num = generate_random_static()

这个函数是一个很好的存储器,它只需要记住上一次的的值,返回一个单一的数字。而不需要存储一整个列表。并且与此类似的函数可以返回却绝育外部事件的连续的值。不方便的是使用这个函数不够方便,且想当不灵活

版本三 定义迭代器类

实质上python2.2的序列都是迭代器,python常见的习惯用法for elem in lst:而实际上是让;lst差生一个迭代器。然后for循环调用这个迭代器的.next()方法,直到遇到StopIteration为止。幸运的是,由于常见的内置类型自动产生它们的迭代器,所以python程序员不需要直到这里发生了什么。实际上,现在字典里有.iterkeys(),.iteritems(),.itervalues()方法产生迭代器。定制类支持直接使用randomwalk_list()以及一次一个元素这种极度节省的randomwalk_static,这是简单易懂的。

class generate_random_class(object):
    def __init__(self):
        self.last = 1
        self.rand = random.random()

    def __iter__(self):
        return self

    def next(self):
        if self.rand < 0.1:
            raise StopIteration
        else:
            while abs(self.last - self.rand) < 0.4:
                print "invalid"
                self.rand = random.random()
            self.last = self.rand
            return self.rand

for item in generate_random_class():
    print item

版本四 生成器版本

上述方法也会产生较多的问题,虽然它避免了产生整个list,大量局部变量的情况,但是当迭代器类或者函数取决于多个数据状态的时候,就需要记录多个数据的状态,这种情况下出现错误的记录较大。所以也不是一种较好的解决方法。使用python自带的生成器yield关键字
在 Python 2.2+ 中,不直接 写生成器。 相反,编写一个函数,当调用它时,返回生成器。这可能看起来有点古怪,但“函数工厂”是 Python 的常见特性,并且“生成器工厂”明显是这个概念性扩展。在 Python 2.2+ 中使函数成为生成器工厂是它主体某处的一个或多个 yield 语句。如果 yield 发生, return 一定只发生在没有伴随任何返回值的情况中。 然而,一个较好的选择是,安排函数体以便于完成所有 yield 之后,执行就“跳转到结束”。但如果遇到 return ,它导致产生的生成器抛出 StopIteration 异常,而不是进一步生成值。

def generate_random_yield():
    last, rand = 1, random.random()
    while rand > 0.1:
        if abs(last - rand) >= 0.4:
            last = rand
            yield rand
        rand = random.random()
    yield rand

for item in generate_random_yield():
    print item

上述做法的好处是逻辑简洁,与正常写法无意,也保证了通用性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值