• Python的生成器和迭代器


    1.Python的生成器(generator)

    为什么要有生成器?

    通过列表生成式,我们可以直接创建一个列表。但是列表所有数据都在内存中,如果有海量数据的话将会非常耗内存。

    如果列表元素按照某种算法推算出来,那我们就可以在循环的过程中不断推算出后续的元素,这样就不必创建完整的list,从而节省大量的空间。

    简单一句话:我又想要得到庞大的数据,又想让它占用空间少,那就用生成器!

    (1)生成器的创建

    1.方法一:列表生成式中,只需要把[] 改成(), 就创建了一个generator

    比如原来的列表生成式,返回的是一个list :

    L = [x * x for x in range(10)]
    

    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

    改为()后,返回的是一个generator

    L = (x * x for x in range(10)])
    

    <generator object at 0x7f674d86c650>

    2.方法二:使用yield关键字创建生成器

    一个函数中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator。

    def func():
        yield
    func()
    

    <generator object func at 0x7fc07d2a7cd0>

    下面将具体讨论yield关键字的用法:

    yield相当于 return 返回一个值,并且记住这个返回的位置,下次迭代时,代码从yield位置继续执行。
    yield关键字 有两个主要的函数让生成器走向下一步:

    • next() 让生成器继续往下一步走。(第一次是开始运行函数,到yield的位置返回值,然后函数暂停;后面每一个next,都是从上次yield返回值的位置开始,继续走到下一个yield返回值的位置,也就是每一次的next开始的地方是接着上一次的next停止的地方执行的)。

    • .send()能传一个值,这个值作为yield表达式整体的结果。换句话说,就是send可以强行修改上一个yield表达式值。比如函数中有一个yield赋值,a = yield 5,第一次迭代到这里会返回5,a还没有赋值。第二次迭代时,使用.send(10),那么,就是强行修改yield 5表达式的值为10,本来是None的,现在是a=10。

    为了更直观的了解整个yield关键字的运行步骤,下面用两个代码示范理解。

    代码1

    def foo():
        print("starting...")
        while True:
            res = yield 4
            print("res:",res)
    g = foo()
    print(next(g))
    print("*"*20)
    print(next(g))
    

    代码的输出为:

    starting...
    4
    ********************
    res: None
    4
    

    1.程序开始执行以后,因为foo函数中有yield关键字,所以foo函数并不会真的执行,而是先得到一个生成器g(相当于一个对象)

    2.直到调用next方法,foo函数正式开始执行,先执行foo函数中的print方法,然后进入while循环

    3.程序遇到yield关键字,然后把yield想想成return,return了一个4之后,程序停止,并没有执行赋值给res操作,此时next(g)语句执行完成,所以输出的前两行(第一个是while上面的print的结果,第二个是return出的结果)是执行print(next(g))的结果,

    4.程序执行print(""20),输出20个*

    5.又开始执行下面的print(next(g)),这个时候和上面那个差不多,不过不同的是,这个时候是从刚才那个next程序停止的地方开始执行的,也就是要执行res的赋值操作,这时候要注意,这个时候赋值操作的右边是没有值的(因为刚才那个是return出去了,并没有给赋值操作的左边传参数),所以这个时候res赋值是None,所以接着下面的输出就是res:None,

    6.程序会继续在while里执行,又一次碰到yield,这个时候同样return 出4,然后程序停止,print函数输出的4就是这次return出的4.

    代码示例2 ,关于.sent()函数

    def foo():
        print("starting...")
        while True:
            res = yield 4
            print("res:",res)
    g = foo()
    print(next(g))
    print("*"*20)
    print(g.send(7))
    

    输出结果

    starting...
    4
    ********************
    res: 7
    4
    

    可以看到,res的值从None变成了7。原因如下:

    5.程序执行g.send(7),程序会从yield关键字那一行继续向下运行,send会把7这个值赋值给res变量

    6.由于send方法中包含next()方法,所以程序会继续向下运行执行print方法,然后再次进入while循环

    7.程序执行再次遇到yield关键字,yield会返回后面的值后,程序再次暂停,直到再次调用next方法或send方法。

    (2)生成器的取值调用

    由于带有 yield 的函数不再是一个普通函数,而是一个生成器generator。

    从生成器中取值有以下两种方式:

    • 可用next()调用生成器对象来取值。next 两种方式 t.next() 或 next(t)。

    • 可用for 循环获取返回值(每执行一次,取生成器里面一个值)

    (基本上不会用next()来获取下一个返回值,而是直接使用for循环来迭代)。
    代码示例

    def foo(a,b):
        num=a
        print("start********")
        while num <= b:
            yield num
            num += 1
    
    • 用next()方法获取返回值
    g=foo(1,3)
    print(next(g))
    print(next(g))
    print(next(g))
    

    start********
    1
    2
    3

    • 用for循环的方法获取返回值
     for i in foo(0,5):
        print(i)
    

    start********
    0
    1
    2
    3
    4
    5

    2.Pyhton的迭代器

    在Python中,迭代器是遵循迭代协议的对象,用来表示一连串数据流。重复调用迭代器的next()方法(或将其传给内置函数 next())将逐个返回数据流中的项。当没有数据可用时则将引发 StopIteration 异常。

    迭代器分为两类,这些可以直接作用于 for 循环的对象统称为可迭代对象: Iterable 。

    • 一类是集合数据类型,如 list 、 tuple 、 dict 、 set 、 str 等;
    • 一类是 generator ,包括生成器和带 yield 的generator function。

    迭代器有两个基本方法:

    • iter() 返回一个迭代器对象
    • next() 逐一返回迭代对象中的项

    代码示例

    list = ['A', 'B', 'C']
    iters = iter(list)
    print(next(iters))
    print(next(iters))
    print(next(iters))
    print(next(iters))
    # list 是长度为3的列表,使用list作为参数返回的迭代器中可迭代的项目也只有3个,当超出可迭代的范围时将引发 StopIteration 异常。
    
    # 迭代器对象可以使用for语句进行遍历。
    list = ['A', 'B', 'C']
    iters = iter(list)
    for i in iters:
        print(i)
    

    总结

    • 凡是可以for循环的,都是Iterable,可迭代对象

    • 凡是可以next()的,都是Iterator,生成器

    • 集合数据类型如list,truple,dict,str,都是Itrable不是Iterator,但可以通过iter()函数获得一个Iterator对象

    3.Python中处理可迭代对象的常用函数

    Python中有几个处理可迭代对象的常用函数,这边总结一下:

    1.map函数

    map() 会根据提供的函数对指定序列做映射,返回映射后的序列的迭代器。

    map(function, iterable, ...)
    

    第一个参数 function ,第二个参数是序列。以参数序列中的每一个元素调用 function 函数,返回 function 函数映射后的新列表。

    2.filter函数

    filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表(返回的是一个迭代器,不是一个List)。

    filter(function, iterable)
    

    该函数接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判断,然后返回 True 或 False,最后将返回 True 的元素放到新列表中。

    3.reduce函数

    reduce() 函数会对参数序列中元素进行累积。

    reduce(function, iterable[, initializer])
    

    函数将一个数据集合(链表,元组等)中的所有数据进行下列操作:用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果。
    比如:

    >>>def add(x, y) :            # 两数相加
    ...     return x + y
    ... 
    >>> reduce(add, [1,2,3,4,5])   # 计算列表和:1+2+3+4+5
    15
    >>> reduce(lambda x, y: x+y, [1,2,3,4,5])  # 使用 lambda 匿名函数
    15
    

    4.sorted函数

    sorted() 函数对所有可迭代的对象进行排序操作。

    sorted(iterable, cmp=None, key=None, reverse=False)
    

    输入一个可迭代对象,指定排序的值,返回一个排序好的序列。

    >>> students = [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
    >>> sorted(students, key=lambda s: s[2])            # 按年龄排序
    [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
     
    >>> sorted(students, key=lambda s: s[2], reverse=True)       # 按降序
    [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
    >>>
    

    5.combination函数

    Python itertools模块可以创建一个迭代器,返回iterable中所有长度为r的子序列,返回的子序列中的项按输入iterable中的顺序排序。

    combinations(iterable, r)
    

    代码示例

    from itertools import combinations
    list1 = [1, 3, 4, 5]
    list2 = list(combinations(list1, 2))
    print(list2)
    
    返回结果:
    [(1, 3), (1, 4), (1, 5), (3, 4), (3, 5), (4, 5)]
    
  • 相关阅读:
    springboot中quartz定时器的postgresql建表语句
    ios设备获取的idfa为 0 原因
    手写实现 js 中的bind,并实现 softBind
    JS 问号?妙用
    手写 js数组reduce
    js 实现桶排序
    手写js 数组打平
    正则表达式匹配多个指定字符串
    手写promise.all和 promise.race
    js防抖和节流实现
  • 原文地址:https://www.cnblogs.com/laiyaling/p/13749975.html
Copyright © 2020-2023  润新知