(python)生成器

一、生成器

    在python中,生成器是一种特殊的迭代器,内部支持生成器协议,不需要明确定义__iter__()和__next__()方法。

生成器通过生成器函数产生,生成器函数可以通过常规的def语名来定义。

    函数内包含有yield关键字,调用函数就不会执行函数体代码,拿到的返回值就是一个生成器对象。

    生成器本质就是迭代器,也就是说生成器的操作法其实就是迭代器一样。
yield作用:
1、为我们提供了一种自定义迭代器的方式,
   可以在函数内用yield关键字,调用函数拿到的结果就是一个生成器,生成器就是迭代器。
2、yield可以像return一样用于返回一个值,区别是return只能返回一次值,但yield可返回多次值.因为yield可以保存函数执行的状态。

def MyRange(n):
    i=0
    while i<n:
        yield i
        i+=1
myrange=MyRange(5)
print(myrange)
print([i for i in myrange])

# 运行结果
# <generator object MyRange at 0x0000000001EB7678>
# [0, 1, 2, 3, 4]

在上面的例子中,我们定义了一个生成器函数,函数返回一个生成器对象,然后就可以通过for语名进行迭代访问。

现在我们来探讨一个生成器函数的执行流程。我们稍微丰富一下上面的例子:

def MyRange(n):
    print('开始执行这个生成器函数')
    i=0
    while i<n:
        print('yield 执行前',i)
        yield i
        i+=1
        print('yield 执行后',i)
    print('生成器函数执行结束')
myrange=MyRange(5)
print('-------------------')
print(myrange.__next__())
print('-------------------')
print(myrange.__next__())
print('-------------------')
print(myrange.__next__())
print('-------------------')
print(myrange.__next__())
print('-------------------')
print(myrange.__next__())
print('-------------------')
print(myrange.__next__())

执行结果:

-------------------
开始执行这个生成器函数
yield 执行前 0
0
-------------------
yield 执行后 1
yield 执行前 1
1
-------------------
yield 执行后 2
yield 执行前 2
2
-------------------
yield 执行后 3
yield 执行前 3
3
-------------------
yield 执行后 4
yield 执行前 4
4
-------------------
yield 执行后 5
生成器函数执行结束
Traceback (most recent call last):
  File "D:/WorkSpace/每日面试/生成器.py", line 36, in <module>
    print(myrange.__next__())
StopIteration

·当调用生器函数的时候,函数只返回了一个生成器对象,并没有执行。

·当__next__()方法第一次被调用的时候,生成器函数才开始执行,执行到yield语句处暂停。__next__()方法的返回值就是yield语名处的参数

·当继续调用__next__()方法的时候,函数将接着一一次停住的yield语外继续执行,到下一个yield处停止,循环直到后面没有yield就抛出StopIteration异常。

生成器表达式

了解生成器表达式,我人首先要知道列表解析式:

[ iter_var for iter_var in iterable if cond_expr]

迭代iterable里所有内容,每一次迭代后,把iterable中满足cond_expr条件的内容放到iter_var中.

例如:生成一个 列表来保存100内能被5整除的数

a=[i for i in range(100) if i%5==0]
print(a)
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]

生成器表达示是python2.4以后引入的,当序列过长,每次中需要获取一个元素时,就应当考虑使用生成器表达式。生成器表达式的语法和列表解析式相类似,只不过生成器表达式不是被[]括起来的,而是用()括起来。

(iter_var for iter_var in iterable if cond_expr)

我们将上面的例子改一下:

a=(i for i in range(100) if i%5==0)
print([i for i in a])
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]

生成器表达示是返回一个生成器,这个生成吕每次计算出一个条目后,把这个条目yiled出来,生成器表达式用了惰性计算,只在在检索进才被赋值,所以在列表比较长的情况下使用它,能够也省内存。

a=(i for i in range(100) if i%5==0)
print([i for i in a])
print([i for i in a])
print('__iter__'in dir(a))
print('__next__'in dir(a))

#运行结果
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]
[]
True
True

从上面我们可以看出,生成器表达式产生在生成器,即是可迭代对象,也是迭代器本身。

递归生成器

生成器可以像函数一个进行递归使用。我们对一个序列进行全排序:

def permutations(li):
    if len(li) == 0:
        yield li
    else:
        for i in range(len(li)):
            li[0], li[i] = li[i], li[0]
            for item in permutations(li[1:]):
                yield [li[0]] + item
    
for item in permutations(range(3)):
    print item

生成器的send和close()方法

send()

从前面我们了解到__next__()方法可恢复生成器的状态并继续执行,而send()也是一个恢复成生器的方法。

在python2.5后yield语名变成了yield表达式,也就是说yield可以有一个值,而这个值就是send()方法的参数,send(None)和__next__()是等效的。

close()

这个方法用于关闭生成器,对关闭的生成器后再调用send()或__next__()方法会抛出StopIteration异常。

看看下面的使用例子:

def MyRange(n):
    print('开始执行这个生成器函数')
    i=0
    while i<n:
        print('yield 执行前',i)
        val=yield i
        i+=1
        print('yield 执行后',i,val)
    print('生成器函数执行结束')
myrange=MyRange(5)
print('-------------------')
print(myrange.__next__())
print('-------------------')
print(myrange.__next__())
print('-------------------')
print(myrange.send('hello'))
print('-------------------')
print(myrange.close())
print('-------------------')
print(myrange.__next__())

执行结果: 

-------------------
开始执行这个生成器函数
yield 执行前 0
0
-------------------
yield 执行后 1 None
yield 执行前 1
1
-------------------
yield 执行后 2 hello
yield 执行前 2
2
-------------------
None
-------------------
Traceback (most recent call last):
  File "D:/WorkSpace/每日面试/生成器.py", line 34, in <module>
    print(myrange.__next__())
StopIteration

总结:

    生成器是一种特殊的迭代器,内部支持了生成器协议,不需要明确定义__iter__()和__next__()方法。

    生成器通过生成器函数产生,生成器函数可以通过常规的def语句来定义,但是不用return返回,而是用yield一次返回一个结果。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值