【python 生成器 面试必备】yield关键字,协程必知必会系列文章--自己控制程序调度,体验做上帝的感觉 1

python生成器系列文章目录

第一章 yield — Python (Part I)



前言

ref:https://medium.com/analytics-vidhya/yield-python-part-i-4dbfe914ad2d
这个老哥把yield讲清楚了,我来学习并且记录一下。


偶尔遇到Yield关键字时,它看起来相当神秘。这里,我们通过查看生成器如何使用yield获取值或将控制权返回给调用者来揭示yield所做的工作。我们也在看生成器generator的不同状态。让我们开始吧。

1. Generator Function 生成器函数

一个函数用了yield表达式后被称为生成器函数。

def happy_birthday_song(name='Eric'):
	yield "Happy Birthday to you"
	yield "Happy Birthday to you"
	yield f"Happy Birthday dear {name}"
	yield "Happy Birthday to you"
birthday_song_gen = happy_birthday_song() # generator creation
print(next(birthday_song_gen)) # prints first yield's value

birthday_song_gen 作为Generator被创建在第七行,相应的,生成器generator的执行通过调用next();
我们获得了yield的1个输出因为仅仅调用了一次next,接着generator是在suspend state(暂停/挂起状态),当另一个next()调用的时候,会激活执行并且返回第二个yield的值。像任何迭代器iterator一样,生成器将会exhausted 当stopIteration is encountered.

def happy_birthday_song(name='Eric'):
    yield "Happy Birthday to you"
    yield "Happy Birthday to you"
    yield f"Happy Birthday dear {name}"
    yield "Happy Birthday to you"
    
birthday_song_gen = happy_birthday_song() # generator creation
print(next(birthday_song_gen)) # prints first yield's value

# print rest of the yield's value
try:
    while True:
        print(next(birthday_song_gen))
except StopIteration:
    print('exhausted...')

2.并发和并行,抢占式和协作式

在这里插入图片描述
在这里插入图片描述
Cooperative multitasking is completely controlled by developer. Coroutine (Cooperative routine) is an example of cooperative multitasking.

Preemptive multitasking is not controlled by developer and have some sort of scheduler involved.

One of the ways to create coroutine in Python is generator.
在python中一种产生协程的做法是generator 生成器。

global 表示将变量声明为全局变量
nonlocal 表示将变量声明为外层变量(外层函数的局部变量,而且不能是全局变量)

def average():
	count = 0
	sum = 0
	def inner(value):
		nonlocal count
		nonlocal sum
		count += 1
		sum += value
		return sum/count
	return inner

def running_average(iterable):
	avg = average()
	for value in iterable:
		running_average = avg(value):
		print(running_average)
iterable = [1,2,3,4,5]
running_average(iterable)

输出:
在这里插入图片描述

The program control flow looks like this:
这个图要好好理解一下:
在这里插入图片描述

2.Let’s implement Producer/Consumer pattern using subroutine:

from collections import deque

def produce_element(dq, n):
    print('\nIn producer ...\n')
    for i in range(n):
        dq.appendleft(i)
        print(f'appended {i}')
        # if deque is full, return the control back to `coordinator`
        if len(dq) == dq.maxlen:
            yield

def consume_element(dq):
    print('\nIn consumer...\n')
    while True:
        while len(dq) > 0:
            item = dq.pop()
            print(f'popped {item}')
        # once deque is empty, return the control back to `coordinator`
        yield

def coordinator():
    dq = deque(maxlen=2)
    # instantiate producer and consumer generator
    producer = produce_element(dq, 5)
    consumer = consume_element(dq)

    while True:
        try:
            # producer fills deque
            print('next producer...')
            next(producer)
        except StopIteration:
            break
        finally:
            # consumer empties deque
            print('next consumer...')
            next(consumer)
            
if __name__ == '__main__':
    coordinator() 

output looks like this:

C:\Users\HP\.conda\envs\torch1.8\python.exe "C:\Program Files\JetBrains\PyCharm 2021.1.3\plugins\python\helpers\pydev\pydevd.py" --multiproc --qt-support=auto --client 127.0.0.1 --port 59586 --file D:/code/python_project/01-coroutine-py-mooc/8/demo_ccc.py
Connected to pydev debugger (build 211.7628.24)
next producer...

 In producer..

next consumer ...

 In consumer... 

popped 0
popped 1
next producer...
next consumer ...
popped 2
popped 3
next producer...
next consumer ...

Process finished with exit code -1

过程解析:
生产2个,消费2个,再生产两个,再消费两个,再生产一个,触发StopIteration,再转向finall 消费1个 整个进程结束。
详细的看英语:
What’s happening? Well, the following thing is happening:

  1. create a limited size deque , here size of 2

  2. coordinator creates an instance of producer generator and also mentioning how many elements it want to generate

  3. coordinator creates an instance of consumer generator

  4. producer runs until deque is filled and yields control back to caller

  5. consumer runs until deque is empty and yields control back to caller

Steps 3 and 4 are repeated until all elements the producer wanted to produce is complete. This coordination of consumer and producer is possible due to we being able to control state of a control flow.

生成器的状态 generator’s states

    from inspect import getgeneratorstate


    def gen(flowers):
        for flower in flowers:
            print(f'Inside loop:{getgeneratorstate(flower_gen)}')
            yield flower


    flower_gen = gen(['azalea', 'Forsythia', 'violas'])
    print(f"After generator creation:{getgeneratorstate(flower_gen)}\n")

    print('getting 1st flower')
    print("--==", next(flower_gen))
    print(f'After getting first flower: {getgeneratorstate(flower_gen)}\n')

    print(f'Get all flowers: {list(flower_gen)}\n')

    print(f'After getting all flowers: {getgeneratorstate(flower_gen)}')

输出:

C:\Users\HP\.conda\envs\torch1.8\python.exe D:/code/python_project/01-coroutine-py-mooc/8/demo_ccc.py
After generator creation:GEN_CREATED

getting 1st flower
Inside loop:GEN_RUNNING
--== azalea
After getting first flower: GEN_SUSPENDED

Inside loop:GEN_RUNNING
Inside loop:GEN_RUNNING
Get all flowers: ['Forsythia', 'violas']

After getting all flowers: GEN_CLOSED

Process finished with exit code 0

We have a handy getgeneratorstate method from inspect module that gives state of a generator. From the output, we see there are four different states:

  1. GEN_CREATED
  2. GEN_RUNNING
  3. GEN_SUSPENDED
  4. GEN_CLOSED
    GEN_CREATED is a state when we instantiate a generator. GEN_RUNNING is a state when a generator is yielding value. GEN_SUSPENDED is a state when a generator has yielded value. GEN_CLOSED is a state when a generator is exhausted.

In summary, yield is used by generators to produce value or give control back to caller and generator has 4 states.

My next article will be sending values to generators!
下一篇文章介绍如何传值到生成器

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值