一些关于python生成器的“坑”

首先,先看代码:

def add(s, x):
    return s + x

def gen():
    for i in range(4):
        yield i

base = gen()
for n in [1, 10]:
    base = (add(i, n) for i in base)

print list(base)

输出的结果是【20,21,22,23】,也许你会有其他答案

那么怎么得出来的呢?
说到生成器,你必须要知道迭代器(iterator),它只能迭代一次,且遵从迭代器协议:
1.定义了__iter__方法,但是必须返回自身
2.定义了next方法,在python3中是__next__,用来返回下一个值,当没有数据可返回时,会抛出一个StopIteration错误
3.可以保持当前的状态

样例
自定义iterator 如何让自定义的类的对象成为迭代器对象,其实就是定义__iter__next 方法:

class DataIter(object):

    def __init__(self, *args):
        self.data = list(args)
        self.ind = 0

    def __iter__(self): #返回自身
        return self

    def next(self): # 返回数据
        if self.ind == len(self.data):
            raise StopIteration
        else:
            data = self.data[self.ind]
            self.ind += 1
            return data

那么为什么可以使用for来迭代迭代器对象呢,原因就是for替我们做了next的活,以及接收StopIteration的处理。
迭代器大概如此,下面介绍主角:优雅的迭代器——生成器(generator)

首先,因为生成器遵循了迭代器协议,所以生成器也是一种迭代器

创建生成器的方式有两种:
1.yield
生成器函数跟普通函数只有一点不一样,就是把 return 换成yield,其中yield是一个语法糖,内部实现了迭代器协议,同时保持状态可以挂起。如下:
记住一点,yield是数据的生产者,而诸如for等是数据的消费者。

def gen():
    print 'begin: generator'
    i = 0
    while True:
        print 'before return ', i
        yield i
        i += 1
        print 'after return ', i

a  = gen()

a.next()

首先,看到while True 时不比惊慌,它只会一个一个被动的执行,不会主动去执行。当调用gen()时,并没有真实执行函数,而是只是返回了一个生成器对象。执行第一次a.next()时,才真正执行函数,执行到yield一个返回值,然后就会挂起,保持当前的名字空间等状态。然后等待下一次的调用,从yield的下一行继续执行。说白了就是你需要数据(被检索)的时候,才会执行。

2.生成器表达式

a = (i for i in range(10))

生成器表达式会创建一个生成器,而且生成器有个特点就是惰性计算, 只有在被检索时候,才会被赋值,正如上边所说。

再来看一个例子:

def multipliers():
    return (lambda x : i * x for i in range(4))
print [m(2) for m in multipliers()]

输出结果为【0,2,4,6】,不清楚则可以看以下等价写法:

def multipliers():
    for x in range(4):
        def cd(i):
            return x*i
        yield cd
print [m(2) for m in multipliers()]

但是需要注意以下例子

def multipliers():
    return [lambda x : i * x for i in range(4)]
print [m(2) for m in multipliers()]

此例输出为【6,6,6,6】而不是【0,2,4,6】,原因是因为闭包的延迟绑定。当循环结束时,i 的值已经是3, 此时结果都是6。不清楚可以看以下等价写法:

def multipliers():
    res = []
    for i in range(4):
        def inner(j):
            return j * i
        res.append(inner)
    return res
print [m(2) for m in multipliers()]

一个解决的方法便是,使用默认参数绑定数值,如下:

def multipliers():
    res = []
    for i in range(4):
        def inner(j, i = i):
            return j * i
        res.append(inner)
    return res
print [m(2) for m in multipliers()]

或者:

def multipliers():
    return [lambda x, i = i : i * x for i in range(4)]
print [m(2) for m in multipliers()]

参考页:http://shomy.top/2016/02/28/python-default-para/
https://segmentfault.com/a/1190000004554823

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值