美文网首页
Python篇-列表生成式、生成器、迭代器与可迭代对象

Python篇-列表生成式、生成器、迭代器与可迭代对象

作者: 左手代码__右手诗 | 来源:发表于2019-03-21 14:51 被阅读0次

    列表生成式、和生成器、迭代器与可迭代对象

    看py代码的时候,每次遇到类似列表生成式与生成器(generator)相关内容的时候,都需要去百度,一次两次三次,每次都不太能记住他们的用法,这里做一个简单总结。

    一、列表生成式

    按照博大精深的中华文化来理解一下字面意思:它是一个表达式,什么表达式?用来生成列表的一个表达式。

    [exp for iter_var in iterable]
    

    嗯,看起来与匿名函数lambda有几分神似。工作过程是这样的:
    1、迭代iterable中的每个元素
    2、把这些元素依次赋值给iter_var
    3、然后通过表达式exp作用于步骤2的每一个值,并生成一个新的元素
    4、把步骤3得到的新的元素组成一个新的列表返回
    类似于下面这个for循环:

    list1 = []
    def exp(arg):#这就是exp表达式
        return arg**2#表达式的作用是计算入参的2次方
    for i in range(5):
        list1.append(exp(i))
    print(list1)
    

    这一段代码可以用列表生成式实现:

    list1 = [x**2 for x in range(5)]
    print(list1)
    

    看起来是不是简单很多?当然列表生成式的作用不仅仅是这个。列表生成式还可以带过滤条件:

    list1 = [x**2 for x in range(5) if x%2 == 0]#增加了一个if表达式,意思是只计算能被2整除的数的2次方
    print(list1)
    

    工作过程在上面的基础上增加了一步:
    1、迭代iterable中的每个元素(此处为range(5))
    2、把这些元素依次赋值给iter_var(x),然后判断if表达式是否成立,成立则进行下一步,不成立则进行下一次迭代
    3、然后通过表达式exp(x**2)作用于步骤2的每一个值,并生成一个新的元素
    4、把步骤3得到的新的元素组成一个新的列表返回
    同样,类似于下面的过程:

    list1 = []
    def exp(arg):#这就是exp表达式
        return arg**2#表达式的作用是计算入参的2次方
    for x in range(5):
        if x%2 == 0:
            list1.append(exp(x))
    print(list1)
    
    例子

    下面有几个例子,实现方式是“使用列表生成式”和“不适用列表生成式”,看下区别:
    eg1:生成一个从3到10的数字列表:

    #不使用列表生成式:
    list1 = list(range(3,11))
    print(list1)
    #或
    list2 = []
    for i in range(3,11):
        list2.append(i)
    print(list2)
    #使用列表生成式
    list3 = [x for x in range(3,11)]
    print(list3)
    

    eg2:生成一个2n+1的数字列表,n为从3到11的数字

    #不使用列表生成式
    list4 = []
    for n in range(3,11):
        list4.append(2*n+1)
    #使用列表生成式
    list4 = [2*n+1 for n in range(3,11)]
    print(list4)
    

    eg3:计算两个集合的全排列,并将结果作为保存至一个新的列表中

    #不使用列表生成式
    L1 = ['香蕉', '苹果', '橙子']
    L2 = ['可乐', '牛奶']
    list5 = []
    for m in L1:
        for n in L2:
            list5.append((m,n))
    print(list5)
    #使用列表生成式
    L1 = ['香蕉', '苹果', '橙子']
    L2 = ['可乐', '牛奶']
    list5 = [(m,n) for m in L1 for n in L2]
    print(list5)
    

    eg4:将一个字典转换成由一组元组组成的列表,元组的格式为(key, value)

    #不使用列表生成式
    D = {'Tom': 15, 'Jerry': 18, 'Peter': 13}
    list6 = []
    for k,v in D.items():
        list6.append((k,v))
    print(list6)
    #使用列表生成式
    D = {'Tom': 15, 'Jerry': 18, 'Peter': 13}
    list6 = [(k,v) for k,v in D.items()]
    print(list6)
    

    二、map函数和filter函数

    map() 会根据提供的函数对指定序列做映射;
    filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表;
    举个例子:
    map()

    def square(x):
        return(x**2)
    r = list(map(square,[1,2,3,4,5]))#map函数返回的是一个对象,需要把这个对象转化成列表,然后打印
    print(r)#输出[1, 4, 9, 16, 25]
    #用匿名函数实现:
    L = [1,2,3,4,5]
    r = list(map(lambda x:x**2,L))
    print(r)
    

    filter()

    def is_odd(x):
        return(x % 2 == 1)
    r = list(filter(is_odd,[1,2,3,4,5]))
    print(r)#输出[1, 3, 5]
    #用匿名函数实现:
    L = [1,2,3,4,5]
    r = list(filter(lambda x:x % 2 == 1,L))
    print(r)
    

    还有两个稍微难一点的例子:
    eg5:把一个列表中所有的字符串转换成小写,非字符串元素原样保留

    L = ['TOM', 'Peter', 10, 'Jerry']
    # 用列表生成式实现
    list1 = [x.lower() if isinstance(x, str) else x for x in L]
    
    # 用map()函数实现
    list2 = list(map(lambda x: x.lower() if isinstance(x, str) else x,  L))
    

    eg6:把一个列表中所有的字符串转换成小写,非字符串元素移除

    L = ['TOM', 'Peter', 10, 'Jerry']
    # 用列表生成式实现
    list3 = [x.lower() for x in L if isinstance(x, str)]
    
    # 用map()和filter()函数实现
    list4 = list(map(lambda x: x.lower(), filter(lambda x: isinstance(x, str), L)))
    

    三、生成器(Generator)

    生成器的原理是,按照一种特定的逻辑算法,在调用他时,不断生成后续相应的数据,不调用则不生成。比如生成100万个数字的列表,某种情况下,我们不需要一次全部生成这些数据,而是用到的时候才需要让他生成,这种情况下,如果使用列表生成式或者一次性计算100万个数字的算法的话,就会浪费内存空间,也会使程序的运行时间变长。这个时候可以用到生成器。
    生成器的第一种构造方式,可以直接使用列表生成式来构造,只需要把列表生成式的[]变成()就好。比如:

    g1 = (2*n + 1 for n in range(3, 6))
    

    也可以使用包含yield关键字的函数来构造(包含yield关键字的函数都叫做生成器),比如

    def my_range(start, end):
        for n in range(start, end):
            yield 2*n + 1
    g2 = my_range(3, 6)
    

    可以使用python的type函数,来检验g1、g2是否是生成器

    print(type(g1))#输出为:<class 'generator'>
    print(type(g2))#输出为:<class 'generator'>
    

    生成器的执行过程:
    在函数执行过程中, 遇到yield关键字会中断执行,知道下次调用函数时,在开始从中断的位置开始继续执行。
    生成器的常用调用方式:
    1、调用next()方法
    2、使用循环对生成器对象进行遍历
    使用next()方法调用:

    print(next(g1))
    print(next(g1))
    print(next(g1))
    print(next(g1))
    

    输出为:

    7
    9
    11
    Traceback (most recent call last):
      File "***/generator.py", line 26, in <module>
        print(next(g1))
    StopIteration
    
    print(next(g2))
    print(next(g2))
    print(next(g2))
    print(next(g2))
    

    输出为:

    7
    9
    11
    Traceback (most recent call last):
      File "***/generator.py", line 31, in <module>
        print(next(g2))
    StopIteration
    

    当遍历完生成器,再调用next()函数,程序会抛出一个StopIteration的异常
    使用循环遍历的方法调用:

    for x in g1:
        print(x)
    
    for x in g2:
        print(x)
    

    上述生成结果时一样的

    7
    9
    11
    

    所以,使用循环遍历的方式,最终不会抛出异常,因为for循环结束的时候,就不会再继续循环了,也就不会再继续调用生成器了,也就不会抛出异常。
    最后补充一下关于迭代器(iterator),可迭代对象(iterable),和生成器之间的关系。
    1、可以被类似next()这样的函数不断调用,并且不断返回下一个值的对象称为迭代器
    2、可迭代对象,类似python基础中学到的列表(list),元组(tuple),字典(dict),字符串(str)都是可以迭代的对象,他们都可以用for循环进行遍历
    3、生成器就是一种迭代器
    所以,迭代器和生成器一定是可迭代对象,反之不一定。比如list,dict,str等;他们三者也都可以用for循环去循环遍历,但是生成器和迭代器(或许不应该这样说,因为生成器本身就是迭代器的一种)还可以使用next()方法调用并不断返回下一个值。
    注:上文引用自云游道士的博客(地址:https://www.cnblogs.com/yyds/p/6281453.html),写的很好,在此借鉴一下。有什么谬误也欢迎指出。

    相关文章

      网友评论

          本文标题:Python篇-列表生成式、生成器、迭代器与可迭代对象

          本文链接:https://www.haomeiwen.com/subject/bdafvqtx.html