python中的yield关键字的用法介绍(代码示例)


本文摘自php中文网,作者不言,侵删。

本篇文章给大家带来的内容是关于python中的yield关键字的用法介绍(代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

yield是python的一个关键字,刚接触python的时候对这个关键字一知半解,掌握之后才发现这关键字有大用,本文将对yield的使用方法好好梳理一番。

1 使用yield创建生成器

在python中,生成器是一种可迭代对象,但可迭代对象不一定是生成器。
例如,list就是一个可迭代对象

1

2

3

4

5

6

7

>>> a = list(range(3))

>>> for i in a:

    print(i)

0

1

2

3

但是一个list对象所有的值都是放在内存中的,如果数据量非常大的话,内存就有可能不够用;这种情况下,就可以生成器,例如,python可以用“()”构建生成器对象:

1

2

3

4

5

6

7

8

9

10

11

>>> b = (x for x in range(3))

>>> for i in b:

    print(i)

 

0

1

2

>>> for i in b:

    print(i)

     

>>>

生成器可以迭代的,并且数据实时生成,不会全部保存在内存中;值得注意的是,生成器只能读取一次,从上面的运行结果可以看到,第二次for循环输出的结果为空

在实际编程中,如果一个函数需要产生一段序列化的数据,最简单的方法是将所有结果都放在一个list里返回,如果数据量很大的话,应该考虑用生成器来改写直接返回列表的函数(Effective Python, Item 16).

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

>>> def get_generator():

    for i in range(3):

        print('gen ', i)

        yield i

         

>>> c = get_generator()   

>>> c = get_generator()

>>> for i in c:

    print(i)

     

gen  0

0

gen  1

1

gen  2

2

由上面的代码可以看出,当调用get_generator函数时,并不会执行函数内部的代码,而是返回了一个迭代器对象,在用for循环进行迭代的时候,函数中的代码才会被执行。
除了使用for循环获得生成器返回的值,还可以使用next和send

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

>>> c = get_generator()

>>> print(next(c))

gen  0

0

>>> print(next(c))

gen  1

1

>>> print(next(c))

gen  2

2

>>> print(next(c))

Traceback (most recent call last):

  File "<pyshell#59>", line 1, in <module>

    print(next(c))

StopIteration

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

>>> c = get_generator()

>>> c.send(None)

gen  0

0

>>> c.send(None)

gen  1

1

>>> c.send(None)

gen  2

2

>>> c.send(None)

Traceback (most recent call last):

  File "<pyshell#66>", line 1, in <module>

    c.send(None)

StopIteration

生成器的结果读取完后,会产生一个StopIteration的异常

2 coroutines中使用

yield一个常见的使用场景是通过yield来实现协程,已下面这个生产者消费者模型为例:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

# import logging

# import contextlib

# def foobar():

#     logging.debug('Some debug data')

#     logging.error('Some error data')

#     logging.debug('More debug data')

# @contextlib.contextmanager

# def debug_logging(level):

#     logger = logging.getLogger()

#     old_level = logger.getEffectiveLevel()

#     logger.setLevel(level)

#     try:

#         yield

#     finally:

#         logger.setLevel(old_level)

# with debug_logging(logging.DEBUG):

#     print('inside context')

#     foobar()

# print('outside context')

# foobar()

def consumer():

    r = 'yield'

    while True:

        print('[CONSUMER] r is %s...' % r)

        #当下边语句执行时,先执行yield r,然后consumer暂停,此时赋值运算还未进行

        #等到producer调用send()时,send()的参数作为yield r表达式的值赋给等号左边

        n = yield r #yield表达式可以接收send()发出的参数

        if not n:

            return # 这里会raise一个StopIteration

        print('[CONSUMER] Consuming %s...' % n)

        r = '200 OK'

def produce(c):

    c.send(None)

    n = 0

    while n < 5:

        n = n + 1

        print('[PRODUCER] Producing %s...' % n)

        r = c.send(n)   #调用consumer生成器

        print('[PRODUCER] Consumer return: %s' % r)

    c.send(None)   

    c.close()

c = consumer()

produce(c)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

[CONSUMER] r is yield...

[PRODUCER] Producing 1...

[CONSUMER] Consuming 1...

[CONSUMER] r is 200 OK...

[PRODUCER] Consumer return: 200 OK

[PRODUCER] Producing 2...

[CONSUMER] Consuming 2...

[CONSUMER] r is 200 OK...

[PRODUCER] Consumer return: 200 OK

[PRODUCER] Producing 3...

[CONSUMER] Consuming 3...

[CONSUMER] r is 200 OK...

[PRODUCER] Consumer return: 200 OK

[PRODUCER] Producing 4...

[CONSUMER] Consuming 4...

[CONSUMER] r is 200 OK...

[PRODUCER] Consumer return: 200 OK

[PRODUCER] Producing 5...

[CONSUMER] Consuming 5...

[CONSUMER] r is 200 OK...

[PRODUCER] Consumer return: 200 OK

Traceback (most recent call last):

  File ".\foobar.py", line 51, in <module>

    produce(c)

  File ".\foobar.py", line 47, in produce

    c.send(None)

StopIteration

在上面的例子中可以看到,yield表达式与send配合,可以起到交换数据的效果,

1

2

n = yield r

r = c.send(n)

3 contextmanager中使用

另外一个比较有意思的使用场景是在contextmanager中,如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

import logging

import contextlib

def foobar():

    logging.debug('Some debug data')

    logging.error('Some error data')

    logging.debug('More debug data')

@contextlib.contextmanager

def debug_logging(level):

    logger = logging.getLogger()

    old_level = logger.getEffectiveLevel()

    logger.setLevel(level)

    try:

        yield #这里表示with块中的语句

    finally:

        logger.setLevel(old_level)

with debug_logging(logging.DEBUG):

    print('inside context')

    foobar()

print('outside context')

foobar()

1

2

3

4

5

6

inside context

DEBUG:root:Some debug data

ERROR:root:Some error data

DEBUG:root:More debug data

outside context

ERROR:root:Some error data

在上面的代码中,通过使用上下文管理器(contextmanager)来临时提升了日志的等级,yield表示with块中的语句;

总结

yield表达式可以创建生成器,应该考虑使用生成器来改写直接返回list的函数;

由于生成器只能读取一次,因此使用for循环遍历的时候要格外注意;生成器读取完后继续读的话会raise一个StopIteration的异常,实际编程中可以使用这个异常来作为读取终止的判断依据;

yield一个常见的使用场景是实现协程;通过与send函数的配合,可以起到交换数据的效果;

yield还可以在contextmanager修饰的函数中表示with块中的语句

以上就是python中的yield关键字的用法介绍(代码示例)的详细内容,更多文章请关注木庄网络博客!!

相关阅读 >>

Python是什么

Python matplotlib中文显示参数设置解析_Python

Python关于tkinter模块中类的三种继承方式示例分享

Python如何判断是否为数字字符串

Python如何用控制变量构建对话程序

Python怎么下载

Python安装了怎么用

Python语言实现百度语音识别功能的实例

详解Python的命名规则

Python卸载容易吗

更多相关阅读请进入《Python》频道 >>




打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...