Python之生成器

一、生成器:

可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其他的数据类型需要调用自己内置的 _iter_ 方法),所以生成器就是可迭代对象

 

二、生成器分类以及在 Python 中的表现形式:(Python有两种不同的方式提供生成器)

1.生成器函数:常规函数定义,但是,使用 yield 语句而不是 return 语句返回结果。yield 语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行

2.生成器表达式:类似于列表推到,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表

 

三、生成器的优点:

Python使用生成器对延迟操作提供了支持。所谓延迟操作,是指在需要的时候才产生结果,而不是立即产生结果,这也是生成器的主要好处

优点一、生成器的好处是延迟计算,一次返回一个结果,也就是说,它不会一次生成所有的结果,这对于大数据量处理,将非常有用

优点二、生成器还能有效提高代码的可读性

  理由:

    1.使用生成器以后,代码行数更少。大家要记住如果想把代码写的 PythonIC,在保证代码可读性的前提下,代码行数越少越好

    2.不使用生成器的时候,对于每次结果。我们首先看到的是 result.append(index),其次,才是 index 。也就是说,我们每次看到的是一个列表的 append 操作。只是 append 我们想要的结果。使用生成器的时候,直接 yield index ,少了列表 append 操作的干扰,我们一眼就能看出,代码是要返回 index

 

四、生成器小结:

1、是可迭代对象

2、实现了延迟计算,省内存

3、生成器本质和其他数据类型一样,都是实现了迭代器协议,只不过生成器附上了一个延迟计算省内存的好处,其余的可迭代对象可都没有这点好处

 

五、生成器表达式

*  三元表达式(将简单的条件判断精简写):

变量 = 值一 if 条件一 else 值二

#!/usr/bin/env python
# -*- coding:utf8 -*-

# 三元表达式
# 主体):if name == 'alex',二元:res = 'as',三元(判断条件):else 'asdf'
# 注意没有四元表达式
name = 'alex'
name = 'sadfgsdfa'
res = 'as' if name == 'alex' else 'asdf'
print(res)

#列表解析
student_list = []
for i in range(10):
    student_list.append('学生%s' %i)
print(student_list)

l1 = ['学生%s' %i for i in range(10)]
l2 = ['学生%s' %i for i in range(10) if i>5]
# l2_test = ['学生%s' %i for i in range(10) if i>5 else i]  #没有四元表达式
#三元表达式:
# 主体(一个for循环):for i in range(10),二元:'学生%s' %i,三元(判断条件):if i>5
print(l1)
print(l2)

# 生成器
SCQ = ('student%s' %i for i in range(10))  #生成器表达式
print(SCQ)
print(SCQ.__next__())
print(SCQ.__next__())
print(SCQ.__next__())
print(next(SCQ))
print(next(SCQ))
print(next(SCQ))
print(next(SCQ))
print(next(SCQ))

# 定义函数
def test():
    yield '我'
    yield '儿子'
    yield '孙子'
 g = test() print('来自函数:',g) print(g.__next__()) print(g.__next__())

 

def S():
    print('** 1 **')
    print('** 2 **')
    yield ''
    print('** 3 **')
    yield ''
    print('** 4 **')
    print('** 5 **')
    yield ''

res = S()
print(res.__next__())
print(res.__next__())
print(res.__next__())

 

做包子案例:

 

# # 全部做好才返回:
# def product_baozi():
#     ret = []
#     for i in range(100):
#         ret.append('包子%s' %i)
#     return ret
#
# baozi_list = product_baozi()
# print(baozi_list)

# # 做好一个就返回:
def product_baozi():
    for i in range(100):
        print('正在生产包子')
        yield '包子%s' %i
        print('开始卖包子')
pro_g = product_baozi()
baozi_1 = pro_g.__next__()
print('来了个人 \n',baozi_1)
baozi_2 = pro_g.__next__()
print('来了个人 \n',baozi_2)
baozi_3 = pro_g.__next__()
print('来了个人 \n',baozi_3)

 

 

总结:

1.把列表解析的 [] 换成 () 得到的就是生成器表达式

2.列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存

3.Python不但使用迭代器协议,让 for 循环变得更加通用,大部分内置函数,也是使用迭代器协议访问对象的。例如,sum函数是Python的内置函数,该函数使用迭代器协议访问对象,二生成器实现了迭代器协议,所以,我们可以直接这样计算一系列值的和:

sum(x**2 for x in range(4))

而不用多此一举地先构造一个列表:

sum([x**2 for x in range(4)])

 六、send 语句的使用

*  yield 相当于 return 控制的是函数的返回值

*  yield 的另外一个特性,接受 send 传过来的值

生产者和消费者模型:

import time

def consumer(name):
    print('我是[%s],我准备开始吃包子了' %name)
    while True:
        baozi = yield
        time.sleep(1)
        print('%s 很开心的把[%s]吃掉了' %(name,baozi))

def producer():
    c1 = consumer('asas')
    c1.__next__()
    for i in range(10):
        time.sleep(1)
        c1.send('包子%s' %i)

producer()

 

七、生成器总结

*  语法上和函数类似: 生成器函数和常规函数几乎是一样的。他们都是使用 def 语句进行定义,差别在于,生成器使用 yield 语句可返回一个值,而常规函数使用 return 语句返回一个值

*  自动实现迭代器协议:对于生成器,Python会自动实现迭代器协议,以便应用到迭代背景中(如 for 循环,sum 函数)。由于生成器自动实现了迭代器协议,所以我们可以调用它的 next 方法,并且,在没有值可以返回的时候,生成器自动产生 StopIteration 异常

*  状态挂起:生成器使用 yield 语句返回一个值。yield 语句挂起该生成器函数的状态,保留足够的信息,以便之后从他离开的地方继续执行

*  注意事项:生成器只能遍历一次(母鸡一生只能下一定数量的蛋,下完就死了)

posted @ 2019-05-01 15:28  Jony-2018  阅读(268)  评论(0编辑  收藏  举报