• python 生成器和迭代器介绍


    在正式接触生成器之前,我们先来了解一些概念

     

    容器(container)

    容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个迭代获取,可以用in、not in关键字判断元素是否包含在容器中。通常这类数据结构把所有的元素存储在内存中(也有一些特殊的存在)。

    Python中常见的容器对象:

    1. list ,deque, ...
    2. set, frozensets, ...
    3. dict, defaultdict, OrderedDict, Counter, ...
    4. tuple, nametuple, ...
    5. str

    提示:1.可迭代对象赋予容器一种可以供提取元素的能力

              2.不是所有的容器都是可迭代的

    可迭代对象(iterable)

    如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration)。

    可迭代对象并不局限于容器,如:files,sockets等(处于打开状态)。只要是可用i返回有一个可迭代的对象都可以称之为可迭代对象。

    例:

    x = [1, 2, 3]
    y = iter(x)
    
    print(next(y))
    print(next(y))
    
    print(type(x), type(y))
    

    分析:

           这里x是一个可迭代对象,可迭代对象和容器一样是一种通俗的称呼,并不是指某种具体的数据类型。list,set,dict都是可迭代对象而y则是一个独立的迭代器,迭代器内部有一个状态,该状态用于记录当前迭代坐在的位置,以方便下次迭代的时获取正确的元素。

           迭代器有一种具体的迭代器类型:list_iterator,set_iterator, ...。可迭代对象实现了__iter__()和__next__()方法(python2中是next()方法,python3.x是__next__()方法),这两个方法对应内置函数iter()和next()。__iter__方法返回可迭代对象本身,这使得它既有可迭代对象同时也是一个迭代器。

    迭代器(iterator)

         迭代器(iterator)是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址。迭代器修改了常规指针的接口,所谓迭代器是一种概念上的抽象:那些行为上像迭代器的东西都可以叫做迭代器。然而迭代器有很多不同的能力,它可以把抽象容器和通用算法有机的统一起来。

    迭代器具备以下两种方法:

    • __iter__()
    • __next__()

    而可迭代对象必须具备:

    • __iter__()

    提示:1.对于迭代器来讲,有一个__next__()就够了。在使用for循环时,程序会自动调用即将被处理的迭代器对象,然后使用next()函数,直到检测到一个StopIteration异常。

              2.next()内置函数就是调用对象的方法__next__(),iter()内置函数是调用对象的__iter__()方法。

    案例:

    • 直接调用next()方法

    • 先使用iter(),再调用next()

    上面的例子中,我们都知道列表 t 可以用 for 循环进行取值操作,但是不能被内置函数 next() 来取值,因此,判断 t 是 可迭代对象(iterable); 当 t 通过 iter() 进行包装后,可以调用 next() 查询取值,所以,I 是 迭代器(iterator)。

    除了上述方式可以判断,还有collections模块的判断函数:

    • 可迭代对象判断

    • 迭代器判断

    使用 iter() 将可迭代对象进行转化成迭代器

    小结:我们可以推断,for 循环内部就是先调用 iter() 把 iterable 变成 iterator 再进行循环迭代。

    上面的for循环和while循环等价

    为什么list, dict, set, str等数据类型不是 迭代器(Iterator)?

           因为Python的 Iterator 对象表示的是一个数据流,Iterator对象可以被next()内置函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration异常。可以把这个数据流看做是一个有序序列,但是不能提前知道这个序列的长度,只能不断通过next()内置函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时才会计算。Iterator可以看做是无限大的数据流,eg: 全体自然数,而list,set, str等是不可能存储全部的自然数的。

    补充: Iterator继承自Iterable,从下面的测试中可以很方便的看到Iterator包含__iter__()和next()方法,而Iteratble仅仅包含__iter__()。

    from collections import Iterator, Iterable

    迭代器总结:

    • 用for循环迭代的对象都是Iterable类型
    • 用next()内置函数取值的对象都是Iterator类型,表示一个惰性计算的序列
    • Iterable均可通过内置函数iter(),获得一个Iterator对象
    • for循环内部实现机制

    生成器

          生成器的概念要比迭代器稍显复杂,因为生成器是能够返回一个迭代器的函数,其最大的作用是将输入对象返回为一个迭代器。Python中使用了迭代的概念,是因为当需要循环遍历一个较大的对象时,传统的内存载入方式会消耗大量的内存,不如需要时读取一个元素的方式更为经济快捷。

    生成器是一次生成一个值的特殊类型函数(特殊的迭代器)。可以将其视为可恢复函数。调用该函数将返回一个可用于生成连续 x 值的生成器Generator。

    有两点要先明确:

    • 任意生成器都是迭代器(反之,不成立)
    • 任意生成器,都是一个可以延迟创建值的工厂(可控性)

    生成器的创建

    • 将列表生成式中[]改成()

           通过列表生成式,可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含百万元素的列表,不仅是占用很大的内存空间,如:我们只需要访问前面的几个元素,后面大部分元素所占的空间都是浪费的。

    因此,没有必要创建完整的列表(节省大量内存空间)。在Python中,我们可以采用生成器:边循环,边计算的机制—>generator

    我们该怎么打印元素呢?

    刚刚提到生成器是特殊的迭代器,可以通过next()内置函数来获得generator的下一个返回值:

    generator是保存的算法,每次调用next()内置函数,才能计算出下一个元素。

    next的方式肯定不能用的,正因为generator也是可迭代对象,我们可以使用for循环。

    提示:这里使用for循环,没有出现异常情况,是和迭代器一样的,都是因为for循环的内部机制。

    简单的说就是在函数的执行过程中,yield语句会把你需要的值返回给调用生成器的地方,然后退出函数,下一次调用生成器函数的时候又从上次中断的地方开始执行,而生成器内的所有变量参数都会被保存下来供下一次使用。

    生成器小结:

    • 生成器对象就是一种特殊的迭代器,满足迭代器协议,可以调用next()内置函数;对生成器for 循环时,调用iter()方法返回了生成器对象,然后再不断next()迭代,而iter()和next()都是在yield内部实现的。
    • 生成器创建方式:常见两种(列表生成式、函数关键字)。


  • 相关阅读:
    ASP.NET存储Session的StateServer
    EasyUI中DataGrid构建复合表头
    EasyUI使用DataGrid向服务器传参
    CentOS 7 U盘安装问题解决
    对指定表的某一列数据合并单元格
    常用的一些sql
    Oracle数据库表空间常用操作
    修改Oracle redo.log文件的大小
    UOJ 非传统题配置手册
    UOJ552 【UNR #4】同构判定鸭【线性代数,哈希】
  • 原文地址:https://www.cnblogs.com/shhnwangjian/p/6295904.html
Copyright © 2020-2023  润新知