zoukankan      html  css  js  c++  java
  • (转)python高级:列表解析和生成表达式

    一、语法糖的概念

    “糖”,可以理解为简单、简洁,“语法糖”使我们可以更加简洁、快速的实现这些功能。 只是Python解释器会把这些特定格式的语法翻译成原本那样复杂的代码逻辑

    我们使用的语法糖有:

    • if...else 三元表达式: 可以简化分支判断语句,如 x = y.lower() if isinstance(y, str) else y
    • with语句: 用于文件操作时,可以帮我们自动关闭文件对象,使代码变得简洁;
    • 装饰器: 可以在不改变函数代码及函数调用方式的前提下,为函数增加增强性功能;
    • 列表生成式: 用于生成一个新的列表
    • 生成器: 用于“惰性”地生成一个无限序列
     

    生成器表达式、列表解析式对比

    列表解析式  在需要改变列表而不是需要新建某列表时,可以使用列表解析无返回值

    1.  
      >>> L= [(x+1,y+1) for x in range(3) for y in range(5)]
    2.  
      >>> L
    3.  
      [(1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (3, 1), (3, 2), (3, 3), (3, 4), (3, 5)]
    4.  
      >>> N=[x+10 for x in range(10) if x>5]
    5.  
      >>> N
    6.  
      [16, 17, 18, 19]



    生成器表达式  有返回值

    当序列过长, 而每次只需要获取一个元素时,应当考虑使用生成器表达式而不是列表解析。生成器表达式的语法和列表解析一样只不过生成器表达式是被()括起来的,而不是[],如下:

    1.  
      >>> L= (i + 1 for i in range(10) if i % 2)
    2.  
      >>> L
    3.  
      <generator object <genexpr> at 0xb749a52c>
    4.  
      >>> L1=[]
    5.  
      >>> for i in L:
    6.  
      ... L1.append(i)
    7.  
      ...
    8.  
      >>> L1
    9.  
      [2, 4, 6, 8, 10]

    1. 当需要只是执行一个循环的时候尽量使用循环而不是列表解析,这样更符合python提倡的直观性。

    1.  
      for item in sequence:
    2.  
      process(item)

    2. 当有内建的操作或者类型能够以更直接的方式实现的,不要使用列表解析。例如复制一个列表时,使用:L1=list(L)即可,不必使用: 

    L1=[x for x in L] 
    
     

    3.如果需要对每个元素都调用并且返回结果时,应使用L1=map(f,L), 而不是 L1=[f(x) for x in L]

    对比:

    计算方式

    生成器表达式延迟计算,列表解析式立即计算
     

    内存占用

    从返回值来说,生成器省内存,列表解析式返回新的列表
     
    生成器没有数据,内存占用极少,但是使用的时候,虽然一个个返回数据,但是合起来占用的内存也差不多
     
    列表解析式构造新的列表需要占用内存
     

    计算速度

    单看计算时间,生成器表达式耗时非常短,列表解析式耗时长
     
    但是生成器本身并没有返回任何值,只返回了一个生成器对象
     
    列表解析式构造并返回了一个新的列表
     

    列表解析实例展示

    1.  
      要求:列出1~10中大于等于4的数字的平方
    2.  
      ####################################################
    3.  
      1、普通方法:
    4.  
      >>> L = []
    5.  
      >>> for i in range(1,11):
    6.  
      ...     if i >= 4:
    7.  
      ...         L.append(i**2)
    8.  
      ... 
    9.  
      >>> print L
    10.  
      [16, 25, 36, 49, 64, 81, 100]
    11.  
      ####################################################
    12.  
      2、列表解析
    13.  
      >>>L = [ i**2 for i in range(1,11) if i >= 4 ]
    14.  
      >>>print L
    15.  
      [16, 25, 36, 49, 64, 81, 100]
     
    [python] view plain copy
     
     
    print?
    1. <code class="language-python">要求:列出1~10所有数字的平方除以2的值  
    2. ####################################################  
    3. 1、普通方法  
    4. >>> L = []  
    5. >>> for i in range(1,11):  
    6. ...     L.append(i**2/2)  
    7. ...   
    8. >>> print L  
    9. [0, 2, 4, 8, 12, 18, 24, 32, 40, 50]  
    10. ####################################################  
    11. 2、列表解析  
    12. >>> L = [i**2/for i in range(1,11) ]  
    13. >>> print L  
    14. [0, 2, 4, 8, 12, 18, 24, 32, 40, 50]</code>  
    1.  
      要求:列出1~10所有数字的平方除以2的值
    2.  
      ####################################################
    3.  
      1、普通方法
    4.  
      >>> L = []
    5.  
      >>> for i in range(1,11):
    6.  
      ... L.append(i**2/2)
    7.  
      ...
    8.  
      >>> print L
    9.  
      [0, 2, 4, 8, 12, 18, 24, 32, 40, 50]
    10.  
      ####################################################
    11.  
      2、列表解析
    12.  
      >>> L = [i**2/2 for i in range(1,11) ]
    13.  
      >>> print L
    14.  
      [0, 2, 4, 8, 12, 18, 24, 32, 40, 50]
    1.  
      要求:列出"/var/log"中所有已'.log'结尾的文件
    2.  
      ##################################################
    3.  
      1、普通方法
    4.  
      >>>import os
    5.  
      >>>file = []
    6.  
      >>> for file in os.listdir('/var/log'):
    7.  
      ...     if file.endswith('.log'):
    8.  
      ...         file.append(file)
    9.  
      ... 
    10.  
      >>> print file
    11.  
      ['anaconda.ifcfg.log', 'Xorg.0.log', 'anaconda.storage.log', 'Xorg.9.log', 'yum.log', 'anaconda.log', 'dracut.log', 'pm-powersave.log', 'anaconda.yum.log', 'wpa_supplicant.log', 'boot.log', 'spice-vdagent.log', 'anaconda.program.log']
    12.  
      ##################################################
    13.  
      2.列表解析
    14.  
      >>> import os
    15.  
      >>> file = [ file for file in os.listdir('/var/log') if file.endswith('.log') ]
    16.  
      >>> print file
    17.  
      ['anaconda.ifcfg.log', 'Xorg.0.log', 'anaconda.storage.log', 'Xorg.9.log', 'yum.log', 'anaconda.log', 'dracut.log', 'pm-powersave.log', 'anaconda.yum.log', 'wpa_supplicant.log', 'boot.log', 'spice-vdagent.log', 'anaconda.program.log']
    1.  
      要求:实现两个列表中的元素逐一配对。
    2.  
      1、普通方法:
    3.  
      >>> L1 = ['x','y','z']
    4.  
      >>> L2 = [1,2,3]
    5.  
      >>> L3 = []
    6.  
      >>> for a in L1:
    7.  
      ... for b in L2:
    8.  
      ... L3.append((a,b))
    9.  
      ...
    10.  
      >>> print L3
    11.  
      [('x', 1), ('x', 2), ('x', 3), ('y', 1), ('y', 2), ('y', 3), ('z', 1), ('z', 2), ('z', 3)]
    12.  
      ####################################################
    13.  
      2、列表解析:
    14.  
      >>> L1 = ['x','y','z']
    15.  
      >>> L2 = [1,2,3]
    16.  
      L3 = [ (a,b) for a in L1 for b in L2 ]
    17.  
      >>> print L3
    18.  
      [('x', 1), ('x', 2), ('x', 3), ('y', 1), ('y', 2), ('y', 3), ('z', 1), ('z', 2), ('z', 3)]
    1.  
      1 使用列表解析生成 9*9 乘法表
    2.  
      2
    3.  
      3 print(' '.join([''.join(['%s*%s=%-2s '%(y,x,x*y)for y in range(1,x+1)])for x in range(1,10)]))
    说明:以上实例,使用列表解析比使用普通方法的速度几乎可以快1倍。因此推荐使用列表解析。
    
     
     

    列表生成式与map()、filter()等高阶函数功能对比

    [python] view plain copy
     
     
    print?
    1. <code class="language-python">要求:把一个列表中所有的字符串转换成小写,非字符串元素原样保留  
    2. L = ['TOM', 'Peter', 10, 'Jerry']  
    3. # 用列表生成式实现  
    4. list1 = [x.lower() if isinstance(x, str) else x for x in L]  
    5.   
    6. # 用map()函数实现  
    7. list2 = list(map(lambda x: x.lower() if isinstance(x, str) else x,  L))</code>  
    1.  
      要求:把一个列表中所有的字符串转换成小写,非字符串元素原样保留
    2.  
      L = ['TOM', 'Peter', 10, 'Jerry']
    3.  
      # 用列表生成式实现
    4.  
      list1 = [x.lower() if isinstance(x, str) else x for x in L]
    5.  
       
    6.  
      # 用map()函数实现
    7.  
      list2 = list(map(lambda x: x.lower() if isinstance(x, str) else x, L))
     
    [python] view plain copy
     
     
    print?
    1. <code class="language-python">要求:把一个列表中所有的字符串转换成小写,非字符串元素移除  
    2. L = ['TOM', 'Peter', 10, 'Jerry']  
    3. # 用列表生成式实现  
    4. list3 = [x.lower() for x in L if isinstance(x, str)]  
    5.   
    6. # 用map()和filter()函数实现  
    7. list4 = list(map(lambda x: x.lower(), filter(lambda x: isinstance(x, str), L)))</code>  
    1.  
      要求:把一个列表中所有的字符串转换成小写,非字符串元素移除
    2.  
      L = ['TOM', 'Peter', 10, 'Jerry']
    3.  
      # 用列表生成式实现
    4.  
      list3 = [x.lower() for x in L if isinstance(x, str)]
    5.  
       
    6.  
      # 用map()和filter()函数实现
    7.  
      list4 = list(map(lambda x: x.lower(), filter(lambda x: isinstance(x, str), L)))

    生成器(Generator)

    1. 生成器的作用

    按照某种算法不断生成新的数据,直到满足某一个指定的条件结束。

    2. 生成器的构造方式

    构造生成器的两种方式:

    • 使用类似列表生成式的方式生成 (2*n + 1 for n in range(3, 11))
    • 使用包含yield的函数来生成

    如果计算过程比较简单,可以直接把列表生成式改成generator;但是,如果计算过程比较复杂,就只能通过包含yield的函数来构造generator。

    1.  
      # 使用类似列表生成式的方式构造生成器
    2.  
      g1 = (2*n + 1 for n in range(3, 6))
    3.  
       
    4.  
      # 使用包含yield的函数构造生成器
    5.  
      def my_range(start, end):
    6.  
      for n in range(start, end):
    7.  
      yield 2*n + 1
    8.  
       
    9.  
      g2 = my_range(3, 6)
    10.  
      print(type(g1))
    11.  
      print(type(g2))
    1.  
      输出结果:
    2.  
       
    3.  
      <class 'generator'>
    4.  
      <class 'generator'>


    既然通过列表生成式就可以直接创建一个新的list,那么为什么还要有生成器存在呢?

       生成器中的元素是按照指定的算法推算出来的,只有调用时才生成相应的数据。这样就不必一次性地把所有数据都生成,从而节省了大量的内存空间,这使得其生成的元素个数几乎是没有限制的,并且操作的返回时间也是非常快速的(仅仅是创建一个变量而已)

    生成器 VS 列表推导式

    列表推导式是一次性创建所有数据的,而生成器是生成对象,需要我们调用next方法逐一获取元素。假设我创建的数据很大很大(比如一千万),列表推导式会一次性创建,需要分配很大的内存空间存放这一千万数据,而生成器不会,只是创建了一个生成器对象,所以生成器更节省内存。

    可迭代对象(Iterable)

    可直接用于for循环的对象统称为可迭代对象(Iterable)

    我们已经知道的可迭代(可用于for循环)的数据类型有:

    • 集合数据类型:如list、tuple、dict、set、str等
    • 生成器(Generator)

    迭代器(Iterator)

    Python中的Iterator对象表示的是一个数据流,Iterator可以被next()函数调用被不断返回下一个数据,直到没有数据可以返回时抛出StopIteration异常错误

    Iterable、Iterator与Generator之间的关系

    生成器对象既是可迭代对象,也是迭代器

     

    迭代器对象一定是可迭代对象,反之则不一定: 例如list、dict、str等集合数据类型是可迭代对象,但不是迭代器,但是它们可以通过iter()函数生成一个迭代器对象

    也就是说:迭代器、生成器和可迭代对象都可以用for循环去迭代,生成器和迭代器还可以被next()方函数调用并返回下一个值。

    字典解析

    字典的形式是: {key: val},所以字典解析式也是用花括号括起来的

    1.  
      # 快速生成值是键二倍的字典
    2.  
      ndict = {x: x*2 for x in range(5)}
    3.  
      print ndict
    4.  
      {0: 0, 1: 2, 2: 4, 3: 6, 4: 8}

    集合解析

    python中集合也是用花括号括起来的,所以集合解析式: {x for x in iter}

    1.  
      # 快速生成1-10的集合
    2.  
      nset = {x for x in range(1, 11)}
    3.  
      print nset
    4.  
      {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}


    --------------------- 本文来自 努力一点点坚持一点点 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/sinat_23880167/article/details/78906819?utm_source=copy 

  • 相关阅读:
    jsonp 请求和回传实现
    序列化实现原型模式
    java基础30问
    模板方法模式
    开发思维导图
    策略模式
    使用vue-router设置每个页面的title
    node.js解决中文乱码问题
    Vue-cli proxyTable代理 解决开发环境的跨域问题
    Vue缓存需要被缓存的页面
  • 原文地址:https://www.cnblogs.com/liujiacai/p/9752348.html
Copyright © 2011-2022 走看看