python的yield和yield from

yield

为了理解什么是 yield,你必须理解什么是生成器。在理解生成器之前,让我们先知道什么是迭代

可迭代对象

当你建立了一个列表,你可以逐项地读取这个列表,这叫做一个可迭代对象:

>>> mylist = [1, 2, 3]
>>> for i in mylist :
...    print(i)
1
2
3

mylist 是一个可迭代的对象。当你使用一个列表生成式来建立一个列表的时候,就建立了一个可迭代的对象:

>>> mylist = [x*x for x in range(3)]
>>> for i in mylist :
...    print(i)
0
1
4

使用迭代器会把所有的值都存储到了内存中,如果你有大量数据的话这个方式并不是你想要的。 

生成器

生成器是可以迭代的,但是你 只可以读取它一次 ,因为它并不把所有的值放在内存中,它是实时地生成数据

def get_yield(limit):
    for i in range(limit):
        yield i

t = get_yield(5)
print(t)
# t输出为 <generator object get_yield at 0x000001C69833CEB8>
for num in t:
    print(num)
# 下面这一段不会输出
for num in t:
    print(num)

yield和return很相似,都起到返回值的作用,但是yield所在的函数返回的是一个生成器,return则直接将某个变量返回,并退出函数。要知道,上面的get_yield函数并不是一被调用就马上执行,可以观看下段代码来更好地了解这一点。

import time
def oneByone(limit):
    for i in range(limit):
        print('oneByone里面i的值是:', i)
        yield i

t = oneByone(5)
for j in range(5):
    i = next(t)
    print(
    print('main函数里面i的值是:' , i)
    time.sleep(1)
    print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))

输出结果:

oneByone里面i的值是: 0
main函数里面i的值是: 0
2018-07-30 12:40:54
oneByone里面i的值是: 1
main函数里面i的值是: 1
2018-07-30 12:40:55
oneByone里面i的值是: 2
main函数里面i的值是: 2
2018-07-30 12:40:56
oneByone里面i的值是: 3
main函数里面i的值是: 3
2018-07-30 12:40:57
oneByone里面i的值是: 4
main函数里面i的值是: 4
2018-07-30 12:40:58

具体理解可以参见高级用法部分的next方法

高级用法

1.控制生成器的穷尽(摘自该文章

>>> class Bank(): # let's create a bank, building ATMs
...    crisis = False
...    def create_atm(self) :
...        while not self.crisis :
...            yield "$100"
>>> hsbc = Bank() # when everything's ok the ATM gives you as much as you want
>>> corner_street_atm = hsbc.create_atm()
>>> print(next(corner_street_atm))
$100
>>> print(next(corner_street_atm))
$100
>>> print([next(corner_street_atm) for cash in range(5)])
['$100', '$100', '$100', '$100', '$100']
>>> hsbc.crisis = True # crisis is coming, no more money!
>>> print(next(corner_street_atm))
<type 'exceptions.StopIteration'>
>>> wall_street_atm = hsbc.create_atm() # it's even true for new ATMs
>>> print(next(wall_street_atm))
<type 'exceptions.StopIteration'>
>>> hsbc.crisis = False # trouble is, even post-crisis the ATM remains empty
>>> print(next(corner_street_atm))
<type 'exceptions.StopIteration'>
>>> brand_new_atm = hsbc.create_atm() # build a new one to get back in business
>>> for cash in brand_new_atm :
...    print cash
$100
$100
$100
$100
$100
$100
$100
$100
$100
...

这里面的逻辑是这么一回事:首先实现了一个Bank类,然后定义了类的所有实例共享的成员变量crisis,当crisis为False时,生成器create_atm()可迭代,反之不行。创建一个hsbc实例,首先创建corner_street_atm这个生成器,可以一直使用next(corner_street_atm)来迭代,但是一旦将crisis设为True,且再次调用next(corner_street_atm)时,该生成器就不可迭代了

注意,将crisis设置为True之后,一定要再次调用生成器该生成器才会不可迭代

这里的逻辑其实很好懂,为什么需要再次调用才会不可迭代呢?因为上面的函数并不会一次性执行完,而是使用next则执行一次,所以当你将crisis设置为True时,如果不使用next方法,则一直不会跳出while循环)。
 

2.send和next方法

python生成器有两个主要方法,一个是send一个是next。

在每段代码中,第一个next调用,相当于启动生成器,会从生成器函数的第一行代码开始执行,直到第一次执行完yield语句(第4行)后,跳出生成器函数。然后第二个next调用,进入生成器函数后,从yield语句的下一句语句(第5行)开始执行,然后重新运行到yield语句,执行后,跳出生成器函数后面再次调用next,依此类推。即执行一次next则调用一次生成器函数,直至抛出不可迭代的错误。

1 def consumer():
2     r = 0
3     for i in xrange(3):
4         yield r
5         r = '200 OK'+ str(i)
6
7 c = consumer()
8 n1 = c.next()
9 n2 = c.next()
10 n3 = c.next()

(该段代码摘自该文章

send方法和next方法其实很相似,唯一的区别在于send方法可以传入值,而next方法不能传值,可以这样说,next(c) = c.send(None)

第一次调用时不能使用send发送一个非None的值,否则会出错的,因为没有Python yield语句来接收这个值。

def consumer():
    r = ''
    while True:
        n = yield r # A
        if not n:
            return
        print('[CONSUMER] Consuming %s...' % n)
        r = '200 OK'

def produce(c):
    c.send(None) # B 
    n = 0
    while n < 5:
        n = n + 1
        print('[PRODUCER] Producing %s...' % n)
        r = c.send(n) # C
        print('[PRODUCER] Consumer return: %s' % r)
    c.close()

c = consumer()
produce(c)

运行结果:

[PRODUCER] Producing 1...
[CONSUMER] Consuming 1...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 2...
[CONSUMER] Consuming 2...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 3...
[CONSUMER] Consuming 3...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 4...
[CONSUMER] Consuming 4...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 5...
[CONSUMER] Consuming 5...
[PRODUCER] Consumer return: 200 OK

在该段代码里面,首先是创建了生成器c,然后将c传入produce里面。下面开始讲解produce和consume函数的交互:

首先执行c.send(None),然后进入生成器函数里面,执行到A处(注意,此处没有执行赋值操作,遇见yield就跳回B了),然后从B的末尾继续执行,在C处执行r = c.send(1),然后又跳回到生成器函数的A处,那么send是怎么赋值的呢???这里是将(yield r)当做一个整体,然后将该值赋给n,也就是n=1了,继续执行A后面的语句,直至再次到A处,重复前面的步骤至循环结束。

另外,C处的语句r都会接受到生成器函数返回的r值。

3.yield from

https://www.cnblogs.com/cdma/p/6839640.html

https://blog.csdn.net/chenbin520/article/details/78111399?locationNum=7&fps=1

https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001432090954004980bd351f2cd4cc18c9e6c06d855c498000

猜你喜欢

转载自blog.csdn.net/f156207495/article/details/81290860