• 13-[函数进阶]-列表生成式,生成器&迭代器


     1.列表生成式

    Python一种独特的语法,相当于语法糖的存在,可以帮你在某些场合写出比较精简酷炫的代码。但没有它,也不会有太多的影响。

    语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会。

    • 需求:把a=[0,1,2,3,4,5,6,7,8,9]里面的每个元素+1
    # sb版本
    >>> b = []
    >>> for i in a:b.append(i+1)
    >>> a = b
    
    # 普通版本
    a = [1,3,4,6,7,7,8,9,11]
    
    for index,i in enumerate(a):
        a[index] +=1
    print(a)
    
    # 文艺版本
    >>> a = map(lambda x:x+1,a)
    >>> a
    <map object at 0x02861810>
    >>> list(a)
    [2, 3, 4, 5, 6, 7, 8, 9]
    
    • 装B版本
    >>> a = [i+1 for i in range(10)]
    >>> a
    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

      这样的写法就叫做列表生成式

      面试真题

    看下面代码回答输出的结果是什么?为什么?

    result = [lambda x: x + i for i in range(10)]
    print(result[0](10))
    

      

    这是一个结合了变量作用域、列表推导式和匿名函数的题目,较为复杂,比较考验Python基础知识掌握程度。有同学可能会回答10,其实答案是19,并且result[0~9](10)的结果都是19。

    这是因为函数具有调用时才查找变量的特性。在你没调用它之前,它不会保存也不关心它内部变量的具体值。只有等到你调用它的时候,它才逐一去找这些变量的具体值。这里的result[0]被调用的时候,变量i已经循环完毕,变成9了,而不是想象中的动态0-9值。

    那如果不想要这样的结果,想要i是循环的值怎么办?不要直接引用上层变量,把变量直接传进来。

    result = [lambda x, i=i: x + i for i in range(10)]
    print(result[0](10))
    

      

    2.生成器

    想生成一个存放很多数据的列表,但是又不想内存占用太多,最好每次用一个生成一个

    在Python中,这种一边循环一边计算的机制,称为生成器:generator。

    要创建一个generator,有很多种方法。

    • 方法1,只要把一个列表生成式的[]改成(),就创建了一个generator:
    • 方法2:yield  把函数变成生成器
    >>> list1 = [i for i in range(100)]
    >>> list1
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27
    , 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 
    55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
     87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
    >>> li = (i for i in range(100))
    >>> li
    <generator object <genexpr> at 0x0220C6E8>
    

      

    • 获取生成器的值:next(li) 和 li.__next__()
    >>> li.__next__()
    0
    >>> li.__next__()
    1
    >>> li.__next__()
    2
    >>> next(li)
    8
    >>> next(li)
    9
    >>> next(li)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration
    
    #每次调用next(g)就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

    3.range也是生成器

    Python 2.7.12 (default, Jul  1 2016, 15:12:24) 
    [GCC 5.4.0 20160609] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> 
    >>> range(10)                # 直接生成list
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    >>> 
    >>> xrange(10)               #一个生成器,每次调用取值
    xrange(10)
    >>> 
    >>> list(xrange(10))
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    range(...)
        range(stop) -> list of integers
        range(start, stop[, step]) -> list of integers

    4.打开文件的f也是生成器




    4.斐波那契数列

    (1)2B版本

    a = 0
    b = 1
    
    count = 0
    while True:
        b,a = a+b,b
        print(a)
        if b > 200:
            break
        count += 1

    (2)函数版本

    def fib(max):
        a, b = 0, 1
        count = 0
        while count < max:
            b, a = a+b, b
            print(a)
            count += 1
        return 'done'
    fib(10)     # 打印10次

    5.yield 函数版生成器

      

      

    这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。

    而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次被next()调用时从上次返回的yield语句处继续执行。

       

       

      

     6.生成器send方法

    例子:执行到yield时,gen函数作用暂时保存,返回i的值;temp接收下次c.send("python"),send发送过来的值,c.next()等价c.send(None)

    def fib(max):
        a, b, n = 0, 1, 0
        while n < max:
            send_msg = yield b
            print(send_msg)
            a, b = b, a+b
            n += 1
    
    f = fib(10)
    print(next(f))
    f.send('寄个快递')
    print(next(f))

       

       

       

    In [29]: f.send(None)    #第一次相当于f.__next__()

     7还可通过yield实现在单线程的情况下实现并发运算的效果

    #_*_coding:utf-8_*_
    __author__ = 'Alex Li'
    
    import time
    def consumer(name):
        print("%s 准备吃包子啦!" %name)
        while True:
           baozi = yield
    
           print("包子[%s]来了,被[%s]吃了!" %(baozi,name))
    
    
    def producer(name):
        c = consumer('A')
        c2 = consumer('B')
        c.__next__()
        c2.__next__()
        print("老子开始准备做包子啦!")
        for i in range(10):
            time.sleep(1)
            print("做了2个包子!")
            c.send(i)
            c2.send(i)
    
    producer("alex")
    
    # 通过生成器实现协程并行运算

        

    8.生成器总结

    生成器是这样一个函数,它记住上一次返回时在函数体中的位置。对生成器函数的第二次(或第 n 次)调用跳转至该函数中间,而上次调用的所有局部变量都保持不变。

    生成器不仅“记住”了它数据状态;生成器还“记住”了它在流控制构造(在命令式编程中,这种构造不只是数据值)中的位置。

    生成器的特点:

    1. 节约内存
    2. 迭代到下一次的调用时,所使用的参数都是第一次所保留下的,即是说,在整个所有函数调用的参数都是第一次所调用时保留的,而不是新创建的



    8.迭代器

    可以直接作用于for循环的数据类型:

    • 一类是集合数据类型,如listtupledictsetstr等;
    • 一类是generator,包括生成器和带yield的 generator function。

    (1)可迭代对象

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

    可以使用isinstance()判断一个对象是否是Iterable对象:
    >>> from collections import Iterable
    >>> isinstance([], Iterable)
    True
    >>> isinstance({}, Iterable)
    True
    >>> isinstance('abc', Iterable)
    True
    >>> isinstance((x for x in range(10)), Iterable)
    True
    >>> isinstance(100, Iterable)
    False
    

      

    (2)迭代器:是一种数据流

      可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。

    可以使用isinstance()判断一个对象是否是Iterator对象:
    
    >>> from collections import Iterator
    >>> isinstance((x for x in range(10)), Iterator)
    True
    >>> isinstance([], Iterator)
    False
    >>> isinstance({}, Iterator)
    False
    >>> isinstance('abc', Iterator)
    False
    

      

    生成器都是Iterator对象,但listdictstr虽然是Iterable,却不是Iterator

    listdictstrIterable变成Iterator可以使用iter()函数:

    >>> isinstance(iter([]), Iterator)
    True
    >>> isinstance(iter('abc'), Iterator)
    True
    

      

      

      (3)小结

    凡是可作用于for循环的对象都是Iterable类型;

    凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

    集合数据类型如listdictstr等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

    • Python3的for循环本质上就是通过不断调用next()函数实现的,例如:
    for x in [1, 2, 3, 4, 5]:
        pass
    

      

      实际上完全等价于:

    # 首先获得Iterator对象:
    it = iter([1, 2, 3, 4, 5])
    # 循环:
    while True:
        try:
            # 获得下一个值:
            x = next(it)
        except StopIteration:
            # 遇到StopIteration就退出循环
            break
    

      

  • 相关阅读:
    PHP学习
    python获取命令行参数 启动文件
    SQLServer中char、varchar、nchar、nvarchar的区别
    VBA
    python 爬虫资料
    python乱码问题之爬虫篇
    angularjs component
    通过jQuery Ajax使用FormData对象上传文件
    directive完成UI渲染后执行JS
    交易日志
  • 原文地址:https://www.cnblogs.com/venicid/p/8431221.html
Copyright © 2020-2023  润新知