欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Python生成器及send用法讲解

程序员文章站 2023-04-03 16:16:48
生成器 我们调用一个普通的Python函数时,一般是从函数的第一行代码开始执行,结束于return语句、异常或者函数结束(可以看作隐式的返回None)。一旦函数将控制权交还给调用者,就意味着全部结束。函数中做的所有工作以及保存在局部变量中的数据都将丢失。再次调用这个函数时,一切都将从头创建。 对于在 ......

- 生成器


我们调用一个普通的Python函数时,一般是从函数的第一行代码开始执行,结束于return语句、异常或者函数结束(可以看作隐式的返回None)。一旦函数将控制权交还给调用者,就意味着全部结束。函数中做的所有工作以及保存在局部变量中的数据都将丢失。再次调用这个函数时,一切都将从头创建。 
对于在计算机编程中所讨论的函数,这是很标准的流程。这样的函数只能返回一个值,不过,有时可以创建能产生一个序列的函数还是有帮助的。要做到这一点,这种函数需要能够“保存自己的工作”。 
我说过,能够“产生一个序列”是因为我们的函数并没有像通常意义那样返回。return隐含的意思是函数正将执行代码的控制权返回给函数被调用的地方。而"yield"的隐含意思是控制权的转移是临时和自愿的,我们的函数将来还会收回控制权。

最初的引入是为了让程序员可以更简单的编写用来产生值的序列的代码

▲.生成值的序列

处理无限序列==>占用内存太大==>遇到问题:函数只有一次返回结果的机会,因而必须一次返回所有的结果==>如果get_primes可以只是简单返回下一个值,而不是一次返回全部的值

一个生成器函数的定义很像一个普通的函数,除了当它要生成一个值的时候,使用yield关键字而不是return。如果一个def的主体包含yield,这个函数会自动变成一个生成器(即使它包含一个return)。除了以上内容,创建一个生成器没有什么多余步骤了。

△生成器就是一类特殊的迭代器。作为一个迭代器,生成器必须要定义一些方法(method),其中一个就是next()。如同迭代器一样,我们可以使用next()函数来获取下一个值。

while循环是用来确保生成器函数永远也不会执行到函数末尾的

加入了新的特性

通过send方法来将一个值”发送“给生成器。other = yield foo 这样的语句的意思是,"返回foo的值,这个值返回给调用者的同时,将other的值也设置为那个值

    def get_primes(number):
        while True:
            if is_prime(number):
                number = yield number
            number += 1
    通过这种方式,我们可以在每次执行yield的时候为number设置不同的值。现在我们可以补齐print_successive_primes中缺少的那部分代码:
    def print_successive_primes(iterations, base=10):
        prime_generator = get_primes(base)
        prime_generator.send(None)
        for power in range(iterations):
            print(prime_generator.send(base ** power))

- 理解send

    def Get_allprimes(num):
        while True:
            if prime_num(num):
                print('num',num)
                other = yield num
                print('other',other)
            num += 1
    
    gene = Get_allprimes(34)
    gene.send(None)
    gene.send(61)
    output:
    >>>num 37
    >>>other 61
    >>>num 41

★ 重点:看这句xx = yield yy

摘要:send()的作用是使xx赋值为发送的值(send的参数),然后让生成器执行到下个yield..

使用send(params)需要区分情况。△注意:如果生成器未启动,则必须在使用send()前必须要启动生成器,而启动的方法可以是generator.next()或是generator.send(None)执行到第一个yield处.之后就可以使用send(params)不断传入值了。如果是已启动,则send(params)的作用就是给xx赋值为发送的值(send的参数),然后让生成器执行到下个yield..

为什么需要send(None),也很好理解,因为 生成器还没有走到第一个yield语句,如果我们发生一个真实的值,这时是没有人去“接收”它的。一旦生成器启动了,就对象接受(即=号左边的左值xx接受了),之后就可以使用send(params)不断传入值了

▲注意,每次的send()都会运行到yield语句,但赋值不会执行,只会有返回值,相当于return后就退出函数了,所以在返回值之后的赋值就不会执行了。

在我看来send()的作用是在next()的基础上,多了个给xx赋值的功能。如第二次的gene.send(61)先从上次停住的yield处开始运行,使other 赋值为61,然后执行到下一个yield处===>可以看出当send方法的参数为None时,它与next方法完全等价

More(给还未懂的人更详细的讲解):
  • send(None)启动后send(int类参数),然后再传个send(None)效果是怎样的?
    def Gene():         #生成器函数
        print("ok")
        x = 100         
        print(x)
        first = yield 50    #这里就是send函数的关键
        # send所传递的值其实就是给 =号左边的左值赋值
        print(first)
    
        second = yield x  # 这里试第二个断点
        print(second)
    
        z = 'third'
        third = yield z
        print(third)
     
     
    inst = Gene()                   #创建生成器对象
    output1 = inst.send(None)       #启动生成器,运行到第一个yield
    print(output1)                 #这边的output1获得的是yield的返回值 50
    output2 = inst.send(30)
    print(output2)
    output3 = inst.send(None)

    output:
    >>>ok
    >>>100      ==>print(x)
    >>>50       ==>print(output1)
    >>>30       ==>send(30)后first被赋值为30然后print(first)
    >>>100      ==>print(output2) ==>output = x==100
    >>>None     ==>send(None)second被赋值为None,print(second)==>None
每次的send(params)都会运行到yield语句,但赋值不会执行,只会有返回值。赋值在下一次send(params)时将xx赋值为params,执行过程如下

只有返回值:

Python生成器及send用法讲解

output1接受返回值:

Python生成器及send用法讲解


执行send(params):

Python生成器及send用法讲解

将passvalue赋值为params:

Python生成器及send用法讲解

接受运行到的第二个yield的返回值:

Python生成器及send用法讲解

参考博客