zoukankan      html  css  js  c++  java
  • Py修行路 python基础 (十二) 协程函数应用 列表生成式 生成器表达式

    一、知识点整理:

    1、可迭代的:对象下有_iter_方法的都是可迭代的对象

     迭代器:对象._iter_()得到的结果就是迭代器
     迭代器的特性:
      迭代器._next_() 取下一个值

     优点:
      1.提供了一种统一的迭代对象的方式,不依赖于索引
      2.惰性计算
     缺点:
      1.无法获取迭代器的长度
      2.一次性的,只能往后取值,不能往前退,不能像索引那样去取得某个位置的值

    2、生成器:函数内带有yield关键字,那么这个函数执行的结果就是生成器

      生成器的本质就是迭代器

    def func():
    n=0
      while True:
        yield n
        n+=1
    g = func()
    next(g)

    3、总结yield的功能:
      1、相当于把_iter_和_next_方法封装到函数内部
      2、与return比,return只能返回一次,而yield能返回多次
      3、函数暂停以及继续运行的状态是通过yield保存的

    4、yield的表达式形式:  如:food = yield

    def eater(name):
      print("%s start to eat"%name)
      while True:
        food = yield
        print("%s eat %s"%(name,food))
    e = eater("zhejiangF4")
    next(e)
    e.send("aaa")  

    5、e.send 与 next(e)的区别

    #1.如果函数内yield是表达式形式,那么必须先next(e)

    #2.二者的共同之处是都可以让函数在上一次暂停的位置继续运行,不一样的地方在于send在触发下一次代码的执行时,会顺便给yield传一个值

    二、生成器  协程函数的应用

    1、编写一个装饰器,在不变更原代码 协程函数 的基础上,直接就能给主函数传值!(协程函数第一步需要next()触发,既将触发写入装饰器中!)

     1 def f(func):  #定义装饰器
     2     def f1(*args,**kwargs):
     3         res = func(*args,**kwargs)
     4         next(res)  #触发主函数
     5         return res
     6     return f1
     7 @f
     8 def eater(name):   #主函数
     9     print("%s start to eat"%name)
    10     while True:
    11         food = yield
    12         print("%s eat %s"%(name,food))
    13 e  = eater("zhejiangF4")
    14 e.send("something")   #直接传值

    执行结果:

    1 zhejiangF4 start to eat
    2 zhejiangF4 eat something

     ##在IDE上加上断点,debug运行查看##

    2、递归目录,过滤文件中带有“python”内容的文件,然后将这些文件打印。此段代码实现功能,牵扯到面向过程编程的思想!定义的每一个函数都是环环相扣,犹如一个完整的生产线一样!

    面向过程的编程思想:流水线式的编程思想,在设计程序时,需要把整个流程设计出

    #优点:
    1、体系结构更加清晰
    2、简化程序的复杂度
    #缺点:
    1、可扩展性极其的差,所以说面向过程的应用场景是:不需要经常变化的软件。

     1 import os,time
     2 def init(func):
     3     def wrapper(*args,**kwargs):
     4         res = func(*args,**kwargs)
     5         next(res)
     6         return res
     7     return wrapper
     8 
     9 @init
    10 def search(target):
    11     '找到文件的绝对路径'
    12     while True:
    13         dir_name=yield
    14         #print('车间search开始生产产品:文件的绝对路径')
    15         time.sleep(1)
    16         g = os.walk(dir_name)
    17         for i in g:
    18             for j in i[-1]:
    19                 file_path = "%s\%s"%(i[0],j)
    20                 target.send(file_path)
    21 @init
    22 def opener(target):
    23     '打开文件,获取文件句柄'
    24     while True:
    25         file_path = yield
    26         #print('车间opener开始生产产品:文件句柄')
    27         time.sleep(1)
    28         with open(file_path) as f:
    29             target.send((file_path,f))
    30 @init
    31 def cat(target):
    32     '读取文件内容'
    33     while True:
    34         file_path,f = yield
    35         #print('车间cat开始生产产品:文件的一行内容')
    36         time.sleep(1)
    37         for line in f:
    38             target.send((file_path,line))
    39 @init
    40 def grep(pattern,target):
    41     '过滤一行内容中有无python'
    42     while True:
    43         file_path,line = yield
    44         #print('车间grep开始生产产品:包含python这一行内容的文件路径')
    45         time.sleep(0.2)
    46         if pattern in line:
    47             target.send(file_path)
    48 @init
    49 def printer():
    50     '打印文件路径'
    51     while True:
    52         file_path = yield
    53         #print('车间printer开始生产产品:得到最终的产品')
    54         time.sleep(1)
    55         print(file_path)
    56 g = search(opener(cat(grep('python',printer()))))
    57 g.send('G:\zhang')

    执行结果:

    1 G:zhanga3.txt
    2 G:zhanga1a1.txt
    3 G:zhanga2a2.txt

    三、列表生成式

      1、由来

      在实际编程的情况中,我们常常需要生成一些列表。除了比较低效的用for循环来一个一个往列表中append外,另一个比较好的方法就是:

      python给我们提供了非常强大的创建列表的方式。

    2、语法 
      [expression for item1 in iterable1 if condition1
           for item2 in iterable2 if condition2
       for item3 in iterable3 if condition3
       for itemN in iterableN if conditionN]
    通俗的来讲,列表生成式由三部分拼接组成:当然每次写之前都应该先给出[],然后在里边添加。
    1.expression 指要生成的元素(参数,变量),放在最前面
    2.后面跟上for循环
    3.for循环之后还可以加上if条件判断,以便进行筛选。
    实际使用的过程中,若一个for循环不能完成问题,还可以往下嵌套。
    1)简单代码举例:
    1  egg_list=[]
    2 for i in range(10):
    3     egg_list.append("egg%s"%i)
    4 print(egg_list)
    5 
    6 l=["egg%s"%i for i in range(10)]
    7 print(l)
    执行结果:
    1 ['egg0', 'egg1', 'egg2', 'egg3', 'egg4', 'egg5', 'egg6', 'egg7', 'egg8', 'egg9']
    2 ['egg0', 'egg1', 'egg2', 'egg3', 'egg4', 'egg5', 'egg6', 'egg7', 'egg8', 'egg9']
    2)稍微有点复杂的,不过也好理解。
     1 #将l 和 s 中每一个元素取出,组成一个新的元组,将所有的结果保存在列表中
     2 l = [1,2,3,4]
     3 s = "hello"
     4 l1 = [(num,s1) for num in l if num >3 for s1 in s]
     5 print(l1)
     6 
     7 l2 = []
     8 for num1 in l :
     9     if num1 >3:
    10         for s2 in s :
    11             t = (num1 ,s2)
    12             l2.append(t)
    13 print(l2)
    执行结果:
    1 [(4, 'h'), (4, 'e'), (4, 'l'), (4, 'l'), (4, 'o')]
    2 [(4, 'h'), (4, 'e'), (4, 'l'), (4, 'l'), (4, 'o')]

    通过比较,虽然上边两种方式都可以实现功能,但是可以非常明显的看出:运用传统意义上的循环,去编写代码是非常繁琐复杂的。

    而运用 列表生成式,同样的内容,可以通过一个list快速生成实现功能的代码,同时写出的代码非常简洁。

    3)再举个例子:读取文件的绝对路径

    ①代码:
     1 import os
     2 g = os.walk("G:\zhang")  #拿取文件路径下所有的文件
     3 #print(g)  #g是一个生成器
     4 l = []
     5 for i in g: #获取所有文件的绝对路径
     6     #print(i)  #路径整体以元组的形式打印出来,元组内部是列表(文件路径,文件名,文件)
     7     for j in i[-1]:  #拿取有文件的路径
     8         file_path = "%s\%s" % (i[0], j)
     9         l.append(file_path)
    10 print(l)
    11 
    12 g = os.walk("G:\zhang")
    13 l1 = ["%s\%s" %(i[0], j) for i in g for j in i[-1]]
    14 print(l1)
    ##如果不明白怎么来的,可以将代码拷出去,将print释放,打印的结果即可!文件路径可以随意更改!## 
    ②执行结果:
    1 ['G:\zhang\a3.txt', 'G:\zhang\a1\a1.txt', 'G:\zhang\a2\a2.txt']

    四、生成器表达式

    1、定义:
    生成器表达式,我个人认为还不如叫列表生成器,就是把列表表达式改变了一下,变成了一个生成器。
    而且这种改变非常简单,就是把外[]换成了()就创建了一个generator。
    通过列表生成式,我们可以直接创建一个列表。但受到内存的限制,列表容量肯定是有限的,同时那么庞大的数据流,一下子拿出来什么机器得卡的受不了。
    而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
    所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。
    在Python中,这种一边循环一边计算的机制,称为生成器:generator。
    就昨天所学的生成器的理解来判断:generator生成器保存的是算法,每次通过next()触发取值,并且每次只取一个元素的值,直到计算到最后一个元素。
    没有更多的元素时,就会抛出StopIteration的错误。我们可以通过for循环来迭代它,并且不需要关心StopIteration的错误。

    这种生成器经常运用于:处理文件,读取数据库中大量的数据 的情况之中。
    1、简单代码举例:
     1 #还是下蛋的例子(……跟鸡过不去了……)
     2 l=['egg%s' %i for i in range(100)]
     3 print(l)
     4 
     5 g=l=('egg%s' %i for i in range(1000000000000000000000000000000000000))
     6 print(g)
     7 print(next(g))
     8 print(next(g))
     9 for i in g:
    10     print(i)
    执行结果:
    2、处理文件的代码举例:
     1 #处理文件,去除文件中每行的空格
     2 #传统处理方式,如果数据很大的话,瞬间将内存挤爆……
     3 f=open('a.txt')
     4 l=[]
     5 
     6 for line in f:
     7     line=line.strip()
     8     l.append(line)
     9 print(l)
    10 
    11 f=open('a.txt')
    12 f.seek(0)
    13 l1=[line.strip() for line in f]
    14 print(l1)
    15 
    16 f=open('a.txt')
    17 f.seek(0)
    18 g=(line.strip() for line in f)
    19 print(g)
    20 print(next(g))
    21 
    22 
    23 #list(可迭代对象)  可以将迭代器转换成列表
    24 f=open('a.txt')
    25 g=(line.strip() for line in f)
    26 
    27 l=list(g)
    28 print(l)
    执行结果:
    ['asdfasdfasdfasdfasdf', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '', 'asdfasdfasdfasdf']
    ['asdfasdfasdfasdfasdf', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '', 'asdfasdfasdfasdf']
    <generator object <genexpr> at 0x000000000291B308>
    asdfasdfasdfasdfasdf
    ['asdfasdfasdfasdfasdf', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '123123123123', '', 'asdfasdfasdfasdf']
    <generator object <genexpr> at 0x000000000291B3B8>
    {'name': 'tesla', 'price': '1000000', 'count': '1'}
    {'name': 'lenovo', 'price': '30000', 'count': '3'}
    View Code
    3、应用:声明式编程
    1)求和函数 sum() 可以计算 可迭代的数据的值
    1 #1、求和函数 sum() 可以计算 可迭代的数据的值
    2 print(sum([1,2,3,4])) #直接对列表求和
    3 nums_g=(i for i in range(3)) #生成器
    4 print(sum(nums_g))#求和
    执行结果:
    1 10
    2 3
    2)计算购物清单总价
     1 # 计算购物清单总价
     2 # 1、传统方式
     3 money_l=[]
     4 with open('b.txt') as f:
     5     for line in f:
     6         goods=line.split()  #将文件中的每行以空格分割,然后以列表的形式保存
     7         res=float(goods[-1])*float(goods[-2]) #求和 个数*单价 此处注意数据类型的转换 str -> float
     8         money_l.append(res)  #生成一个总价的列表
     9 print(money_l)  #打印列表
    10 print(sum(money_l))#求总价
    11 #
    12 # 2、列表生成器 方法  将上边的代码用声明式编程代替
    13 f=open('b.txt')
    14 g=(float(line.split()[-1])*float(line.split()[-2]) for line in f)
    15 print(sum(g))
    16 #
    执行结果:
    1 [30.0, 1000000.0, 6000.0, 90000.0, 30.0]
    2 1096060.0
    3 1096060.0
    3)数据库查询的功能(文件数据,string)得到的内容是[{},{}]形式,列表套字典的形式。
     1 res=[]
     2 with open('b.txt') as f:
     3     for line in f:
     4         # print(line)
     5         l=line.split() #把每行处理成列表
     6         # print(l)
     7         d={}  #先定义一个空字典
     8         d['name']=l[0]  #往字典内赋值
     9         d['price']=l[1]  #往字典内赋值
    10         d['count']=l[2]  #往字典内赋值
    11         res.append(d)  #将新创建的字典写到列表中
    12 print(res)  #打印结果
    13 #
    14 # 生成器表达式 方式 处理
    15 with open('b.txt') as f:
    16     res=(line.split() for line in f)  #得到一个列表生成器 大列表,文件内所有内容都在
    17     #print(res) #查看类型 生成器
    18     dic_g=({'name':i[0],'price':i[1],'count':i[2]} for i in res)  #对迭代器进行取值,拿到每个小列表,组成一个新的字典,存放在新的列表中
    19     print(dic_g)#查看类型  生成器
    20     apple_dic=next(dic_g)  #取第一值  前提是知道第一个是什么
    21     print(apple_dic['count'])
    执行结果:
    1 [{'name': 'apple', 'price': '10', 'count': '3'}, {'name': 'tesla', 'price': '1000000', 'count': '1'}, {'name': 'mac', 'price': '3000', 'count': '2'}, {'name': 'lenovo', 'price': '30000', 'count': '3'}, {'name': 'chicken', 'price': '10', 'count': '3'}]
    2 <generator object <genexpr> at 0x00000000028EB360>
    3 3

    此处有一个非常有趣的问题,昨天所学,我们知道文件本身就是一个迭代器。

    next()取值之后,会将文件关闭。往后就无法再取值,所以会有I/O错误 没法读取 文件关闭的报错。
    所以调用文件的话,建议用 f = open("b.txt") 或是next()触发取值的话,缩近放在里边。

    4)取出单价>10000 大体不变,只是将每行组成的列表,格式化 转换成字典的时候进行过滤,取出满足条件的内容
    1 # 取出单价>10000  大体不变,只是将每行组成的列表,格式化 转换成字典的时候进行过滤,取出满足条件的内容
    2 with open('b.txt') as f:
    3     res=(line.split() for line in f)
    4     # print(res)
    5     dic_g=({'name':i[0],'price':i[1],'count':i[2]} for i in res if float(i[1]) > 10000)
    6     print(dic_g)
    7     #print(list(dic_g))  #直接取值
    8     for  i in dic_g:  #for循环取值
    9         print(i)

    执行结果:

    1 <generator object <genexpr> at 0x00000000026BB3B8>
    2 {'name': 'tesla', 'price': '1000000', 'count': '1'}
    3 {'name': 'lenovo', 'price': '30000', 'count': '3'}
  • 相关阅读:
    WIN7中易语言的显示问题
    Win8关闭metro UI的方法汇总
    Win8快速更改输入法不习惯问题
    win7 win8和谷歌浏览器内核的抓包工具
    运算符重载复习
    1002
    [STL] 成员函数VS算法函数
    ACM448
    nuaa acm 1138
    怎么呢...
  • 原文地址:https://www.cnblogs.com/zh605929205/p/6700760.html
Copyright © 2011-2022 走看看