生成器

生成器

1. 什么是生成器

器乃工具也,生成器就是就来生成某种东西的工具

生成器实际上本质就是迭代器,也是惰性取值,也节省内存

2. 为什么要有生成器

既然生成器本质就是迭代器,那么python为什么还要搞一个生成器呢?他俩有啥区别?实际上迭代器是通过可迭

代对象转换出来的,不是我们想怎么搞就怎么,要取决于可迭代对象,我靠,可迭代对象不是可以自己定义吗?

这样实际上也自定义了迭代器,不也可以吗?python之父可能觉得不爽,我直接搞一个自定义的迭代器,不通过

自定义可迭代对象转换这样不好吗?看不惯就造,于是有了生成器

生成器就是自定义的迭代器,它不依赖可迭代对象,直接造出来,器是工具,工具可以通过函数造出来,所以通过自定义一个特殊的函数,来实现生成器

3. 生成器怎么用

3.1 yield关键字

在函数内部只要有yield关键字,调用函数之后不会执行函数体代码,而是返回一个生成器对象

def func():
    print('第一次')
    yield 1
    print('第二次')
    yield 2
    print('第三次')
    yield 3
    print('第四次')


g = func()	 #  生成器
print(g)     # <generator object func at 0x000001E30BDDFF10>

3.2 __next__方法

生成器调用__next__方法也会从里面取出一个值

def func():
    print('第一次')
    yield 1
    print('第二次')
    yield 2
    print('第三次')
    yield 3
    print('第四次')


g = func()
print(g.__next__())   # 1
# 生成器调用__next__方法会触发函数体代码的运行,然后遇到yield停下来,将yield后的值当做本次调用的结果返回

yieldreturn关键字都可以返回值,但是return会终止函数 , 但是yield不会 , 而是夯住 , 等待下一次取值

也就说yield做到了函数体代码中止,是不是很牛逼,以前的函数体代码都是一股烟全执行完毕,现在我们可以通

过yield关键字,让他先执行一部分,然后主程序去执行其他代码,然后又回来执行函数体剩下的代码,做到了

从代码的切换从函数内切换到外面然后还能切换回来,我靠,比闭包还cool

同样生成器当你取完值再取里面就没有值了,再取就报错。

def func():
    print('第一次')
    yield 1
    print('第二次')
    yield 2
    print('第三次')
    yield 3
    print('第四次')


g = func()
print(g.__next__())   # 1
print(g.__next__())   # 2
print(g.__next__())   # 3
print(g.__next__())   # 报错

3.3 for循环生成器

生成器自然而然也是可以被for循环的

def func():

    yield 1

    yield 2

    yield 3


g = func()

for i in g:
    print(i)   # 1 2 3

3.4 __iter__方法

同样生成器调用iter方法也是返回自己本身

def func():
    print('第一次')
    yield 1
    print('第二次')
    yield 2
    print('第三次')
    yield 3
    print('第四次')


g = func()
print(g)              # <generator object func at 0x00000170511EFF10>
print(g.__iter__())   # <generator object func at 0x00000170511EFF10>

4. 为yield传值

yield关键字不但可以返回值,你还可以在函数外部为其传值,不过要通过指定的方法send()

def dog(name):
    print('%s准备吃东西啦...' % name)
    while True:
        # x拿到的是yield接收到的值
        x = yield 111 # x ='肉包子'
        print('%s吃了%s' % (name, x))


g = dog('tom')    # 得到一个生成器赋值给g
g.send(None)      # 等同于g.__next__(),让函数挂起,就是初始化,当刚得到生成器的时候,光标在开头,初始化光标应该在yield后面
res = g.send('肉包子')  
print(res)

然后函数体内部的代码执行到下一次yield右边,挂起,同样会把后面的返回值111返回给这次的调用者即

g.seng("肉包子")

注意 :

刚得到的生成器不能为其yield传非None值,要么你传一个None值,要么你调用g.__next__()
记住每次send()都是在为yield传值,但是,调用send()的返回值是yield后面的值

5. 小总结

优点

  • Python使用生成器提供了延迟操作。所谓延迟操作,是指在需要的时候才产生结果,而不是立即产生

    结果。这对于大数据量处理,将会非常有用。

  • 除了延迟计算,生成器还能有效提高代码可读性。

缺点

  • 只能遍历一次

生成器迭代器都可以反复创建,迭代器取决于可迭代对象的存在,只要可迭代器对象在,你就可以造迭代器

取完值,你想还取你就再调用一次__iter__方法,又会返回一个迭代器,当然你取完值,迭代器里面虽然没有

值了,但是你还以再调用__iter__方法,循环往复。而生成器取完里面的值,你就要在定义一个新的生成器,才

能继续愉快的取值了,也有人叫这样定义的生成器叫生成器函数

6. 小练习

6.1 自定义一个可以无限生成值的生成器

def nb_g():
    count = 0
    while 1:
        count += 1
        yield count

6.2 自定义一个range()生成器

def my_range(start,stop,step):
    while start < stop
    	yield start
        strat += step
原文地址:https://www.cnblogs.com/xcymn/p/14081671.html