19python学习 生成器 及函数 及作业讲解

 
https://www.cnblogs.com/luchuangao/articles/6685626.html
import time
def test():
   print('开始生孩子啦。。。。。。')
   print('开始生孩子啦。。。。。。')
   print('开始生孩子啦。。。。。。')
   yield '我' #return
   time.sleep(3)
   print('开始生儿子啦')
   yield '儿子'

   time.sleep(3)
   print('开始生孙子啦')
   yield '孙子'

#
res=test()
print(res)
print(res.__next__()) #test()   生成器基于迭代器协议  会保留函数的状态 下次接着运行下个yield
print(res.__next__()) #test()
print(res.__next__()) #test()
输出
<generator object test at 0x00000000026F92B0>
开始生孩子啦。。。。。。    1
开始生孩子啦。。。。。。     1
开始生孩子啦。。。。。。        1
我                              1
开始生儿子啦                  2
儿子                            2
开始生孙子啦                  3
孙子                           3


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    #i=1      并不需要完整产生一个列表,要一个取一个
      print('开始卖包子')
pro_g=product_baozi()

baozi1=pro_g.__next__()
print('来了一个人吃包子',baozi1)
#加代码
baozi1=pro_g.__next__()
print('来了一个人吃包子',baozi1)
输出
正在生产包子
来了一个人吃包子 一屉包子0
开始卖包子
正在生产包子
来了一个人吃包子 一屉包子1


def xiadan():
   ret=[]
   for i in range(10000):
      ret.append('鸡蛋%s' %i)
   return ret

print(xiadan())
缺点1:占空间大
缺点2:效率低

def xiadan():
   for i in range(5):
      yield '鸡蛋%s' %i       #下一个给一个,不占过多内存

alex_lmj=xiadan()
print(alex_lmj.__next__())
print(alex_lmj.__next__())
print(alex_lmj.__next__())
print(alex_lmj.__next__())
print(alex_lmj.__next__())
print(alex_lmj.__next__())


for jidan in alex_lmj:
   print(jidan)


jidan=alex_lmj.__next__()
jidan=alex_lmj.__next__()
jidan=alex_lmj.__next__()
jidan=alex_lmj.__next__()
jidan=alex_lmj.__next__()
jidan=alex_lmj.__next__()



print('zhoushaochen 取鸡蛋',jidan)
 
 

综上已经对生成器有了一定的认识,下面我们以生成器函数为例进行总结

  • 语法上和函数类似:生成器函数和常规函数几乎是一样的。它们都是使用def语句进行定义,差别在于,生成器使用yield语句返回一个值,而常规函数使用return语句返回一个值
  • 自动实现迭代器协议:对于生成器,Python会自动实现迭代器协议,以便应用到迭代背景中(如for循环,sum函数)。由于生成器自动实现了迭代器协议,所以,我们可以调用它的next方法,并且,在没有值可以返回的时候,生成器自动产生StopIteration异常
  • 状态挂起:生成器使用yield语句返回一个值。yield语句挂起该生成器函数的状态,保留足够的信息,以便之后从它离开的地方继续执行

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

1 #列表解析
2 sum([i for i in range(100000000)])#内存占用大,机器容易卡死
3 
4 #生成器表达式
5 sum(i for i in range(100000000))#几乎不占内存

生成器只能遍历一次, 母鸡下完蛋就死了

各省人口占比


def get_polulation():  #特定功能,写成函数形式
    with open('人口普查', 'r', encoding='utf-8') as f:
        for i in f:
            yield i      #比ret.append(i)更快用内存更少

g=get_polulation()
print(g.__next__())       #{'name':'北京','population':10}
g=get_polulation()
g.__next__()
g.__next__()
g.__next__()
g.__next__()
print(g.__next__()['population'])  #不行,字符串字符串必须通过整型来取,g.__next__()实际上不是字典,而是长成字典样子的字符串,要转成字典
s1=eval(g.__next__())  #eval把字符串里面的表达式运算出来,以及字符串里面的格式表达出来,
print(type(s1))    #<class 'dict'>  成功转成字典
print(s1['population'])  #1000000  成功取到数据

res=0
for p in g:
    p_dic=eval(p)  #取出某一行的字典样子的字符串转换成字典
    print(p_dic['population'])  #显示字典中population的value
    res+=p_dic['population']
print(res)
# 等效
all_pop=sum(eval(i)['population'] for i in g)  #for i in g  eval(i)['population'] 转成字典后取popula求和
print(all_pop)
#
无用   生成器只能遍历一次, 母鸡下完蛋就死了
for p in g:
    print('%s %%' %eval(p)['population']/all_pop)  #没有输出啊,因为前边 for i in g已经历遍了文件,
    # 导致光标已经是在最后了,已经没有数据可以给我们了,
    # 如果想要保留数据的话要在next函数那里给赋值来记录下来

生产消费模型

import time
def producer():   #生产包子,全生产后操作
    ret=[]
    for i in range(100):
        time.sleep(0.1)
        ret.append('包子%s' %i)
    return ret

def consumer(res):
    for index,baozi in enumerate(res):  #enumerate输出索引 和内容‘包子n’
        time.sleep(0.1)
        print('第%s个人,吃了%s' %(index,baozi))

res=producer()  #拿到100个包子list
consumer(res)  #循环包子


用生成器改写

yield 3相当于r eturn 控制的是函数的返回值
x=yield的另外一个特性,接受send传过来的值,赋值给x
def test():
    print('开始啦')
    firt=yield 1#return 1   first='sbsbsb'  'sbsbsb'是t.send('sbsbsb')中的'sbsbsb'
    print('第一次',firt)
    yield 2
    print('第二次')

t=test()     #这一步还没有开始运行,只执行这一步的话不会有print
print(t)     #拿到生成器对象 <generator object test at 0x00000000028F8200>
res=t.__next__() #next(t) 这两种方法
print(res)
res=t.send('sbsbsb')       #第三种方法触发生成器 从光标位置的yield执行到下一个yield,
并且将括号内的'sbsbsb'值赋值给函数中的firt=yield 1  中的firt
# res=t.send('函数停留在first那个位置,我就是给first赋值的')
print(res)





def producer():
    ret=[]
    for i in range(100):
        time.sleep(0.1)
        ret.append('包子%s' %i)
    return ret


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

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


我是[wupeiqi],我准备开始吃包子了
我是[yuanhao_SB],我准备开始吃包子了
wupeiqi 很开心的把【包子 0】吃掉了
yuanhao_SB 很开心的把【包子 0】吃掉了
wupeiqi 很开心的把【包子 1】吃掉了
yuanhao_SB 很开心的把【包子 1】吃掉了
.....

  作业后边补

猜你喜欢

转载自blog.csdn.net/yunyupianlan/article/details/81037915