zoukankan      html  css  js  c++  java
  • Python 生成器和迭代器,yield语句

    什么是迭代器

    顾名思义,迭代器就是用于迭代操作(for 循环)的对象,它像列表一样可以迭代获取其中的每一个元素,任何实现了 __next__ 方法 (python2 是 next)的对象都可以称为迭代器。

    它与列表的区别在于,构建迭代器的时候,不像列表把所有元素一次性加载到内存,而是以一种延迟计算(lazy evaluation)方式返回元素,这正是它的优点。比如列表含有中一千万个整数,需要占超过400M的内存,而迭代器只需要几十个字节的空间。因为它并没有把所有元素装载到内存中,而是等到调用 next 方法时候才返回该元素(按需调用 call by need 的方式,本质上 for 循环就是不断地调用迭代器的next方法)。

    以斐波那契数列为例来实现一个迭代器:

    class Fib:
        def __init__(self, n):
            self.prev = 0
            self.cur = 1
            self.n = n
    
        def __iter__(self):
            return self
    
        def __next__(self):
            if self.n > 0:
                value = self.cur
                self.cur = self.cur + self.prev
                self.prev = value
                self.n -= 1
                return value
            else:
                raise StopIteration()
        # 兼容python2
        def __next__(self):
            return self.next()
    
    f = Fib(10)
    print([i for i in f])
    #[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

    什么是生成器

    知道迭代器之后,就可以正式进入生成器的话题了。普通函数用 return 返回一个值,和 Java 等其他语言是一样的,然而在 Python 中还有一种函数,用关键字 yield 来返回值,这种函数叫生成器函数,函数被调用时会返回一个生成器对象,生成器本质上还是一个迭代器,也是用在迭代操作中,因此它有和迭代器一样的特性,唯一的区别在于实现方式上不一样,后者更加简洁

    最简单的生成器函数:

    >>> def func(n):
    ...     yield n*2
    ...
    >>> func
    <function func at 0x00000000029F6EB8>
    >>> g = func(5)
    >>> g
    <generator object func at 0x0000000002908630>
    >>>

    func 就是一个生成器函数,调用该函数时返回对象就是生成器 g ,这个生成器对象的行为和迭代器是非常相似的,可以用在 for 循环等场景中。注意 yield 对应的值在函数被调用时不会立刻返回,而是调用next方法时(本质上 for 循环也是调用 next 方法)才返回

    >>> g = func(5)
    >>> next(g)
    10
    
    >>> g = func(5)
    >>> for i in g:
    ...     print(i)
    ...
    10

    那为什么要用生成器呢?显然,用生成器在逼格上要比迭代器高几个等级,它没有那么多冗长代码了,而且性能上一样的高效,为什么不用呢?来看看用生成器实现斐波那契数列有多简单。

    def fib(n):
        prev, curr = 0, 1
        while n > 0:
            n -= 1
            yield curr
            prev, curr = curr, curr + prev
    
    print([i for i in fib(10)])
    #[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

    生成器表达式

    器表达式与列表推导式长的非常像,但是它俩返回的对象不一样,前者返回生成器对象,后者返回列表对象。

    >>> g = (x*2 for x in range(10))
    >>> type(g)
    <type 'generator'>
    >>> l = [x*2 for x in range(10)]
    >>> type(l)
    <type 'list'>

    前面已经介绍过生成器的优势,就是迭代海量数据时,显然生成器更合适。

     

    Python中生成器和迭代器的区别(代码在Python3.5下测试):
    Num01–>迭代器
    定义:
    对于list、string、tuple、dict等这些容器对象,使用for循环遍历是很方便的。在后台for语句对容器对象调用iter()函数。iter()是python内置函数。
    iter()函数会返回一个定义了next()方法的迭代器对象,它在容器中逐个访问容器内的元素。next()也是python内置函数。在没有后续元素时,next()会抛出一个StopIteration异常,通知for语句循环结束。

    迭代器是用来帮助我们记录每次迭代访问到的位置,当我们对迭代器使用next()函数的时候,迭代器会向我们返回它所记录位置的下一个位置的数据。实际上,在使用next()函数的时候,调用的就是迭代器对象的_next_方法(Python3中是对象的_next_方法,Python2中是对象的next()方法)。所以,我们要想构造一个迭代器,就要实现它的_next_方法。但这还不够,python要求迭代器本身也是可迭代的,所以我们还要为迭代器实现_iter_方法,而_iter_方法要返回一个迭代器,迭代器自身正是一个迭代器,所以迭代器的_iter_方法返回自身self即可。

    一些术语的解释:
    1,迭代器协议:对象需要提供next()方法,它要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代。

    2,可迭代对象:实现了迭代器协议对象。list、tuple、dict都是Iterable(可迭代对象),但不是Iterator(迭代器对象)。但可以使用内建函数iter(),把这些都变成Iterable(可迭代器对象)。

    3,for item in Iterable 循环的本质就是先通过iter()函数获取可迭代对象Iterable的迭代器,然后对获取到的迭代器不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束

    Python自带容器对象案例:
    # 随便定义一个list
    listArray=[1,2,3]
    # 使用iter()函数
    iterName=iter(listArray)
    print(iterName)
    # 结果如下:是一个列表list的迭代器
    # <list_iterator object at 0x0000017B0D984278>

    print(next(iterName))
    print(next(iterName))
    print(next(iterName))
    print(next(iterName))#没有迭代到下一个元素,直接抛出异常
    # 1
    # 2
    # 3
    # Traceback (most recent call last):
    # File "Test07.py", line 32, in <module>
    # StopIteration

    Python中一个实现了_iter_方法和_next_方法的类对象,就是迭代器,如下案例是计算菲波那切数列的案例

    class Fib(object):
    def __init__(self, max):
    super(Fib, self).__init__()
    self.max = max

    def __iter__(self):
    self.a = 0
    self.b = 1
    return self

    def __next__(self):
    fib = self.a
    if fib > self.max:
    raise StopIteration
    self.a, self.b = self.b, self.a + self.b
    return fib

    # 定义一个main函数,循环遍历每一个菲波那切数
    def main():
    # 20以内的数
    fib = Fib(20)
    for i in fib:
    print(i)

    # 测试
    if __name__ == '__main__':
    main()
    解释说明:

    在本类的实现中,定义了一个_iter_(self)方法,这个方法是在for循环遍历时被iter()调用,返回一个迭代器。因为在遍历的时候,是直接调用的python内置函数iter(),由iter()通过调用_iter_(self)获得对象的迭代器。有了迭代器,就可以逐个遍历元素了。而逐个遍历的时候,也是使用内置的next()函数通过调用对象的_next_(self)方法对迭代器对象进行遍历。所以要实现_iter_(self)和_next_(self)这两个方法。

    而且因为实现了_next_(self)方法,所以在实现_iter_(self)的时候,直接返回self就可以。

    总结一句话就是:
    在循环遍历自定义容器对象时,会使用python内置函数iter()调用遍历对象的_iter_(self)获得一个迭代器,之后再循环对这个迭代器使用next()调用迭代器对象的_next_(self)。

    注意点:_iter_(self)只会被调用一次,而_next_(self)会被调用 n 次,直到出现StopIteration异常。

    Num02–>生成器
    作用:
    >延迟操作。也就是在需要的时候才产生结果,不是立即产生结果。

    注意事项:
    >生成器是只能遍历一次的。
    >生成器是一类特殊的迭代器。

    分类:
    第一类:生成器函数:还是使用 def 定义函数,但是,使用yield而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行。

    如下案例加以说明:

    # 菲波那切数列
    def Fib(max):
    n, a, b = 0, 0, 1
    while n < max:
    yield b
    a, b = b, a + b
    n = n + 1
    return '亲!没有数据了...'
    # 调用方法,生成出10个数来
    f=Fib(10)
    # 使用一个循环捕获最后return 返回的值,保存在异常StopIteration的value中
    while True:
    try:
    x=next(f)
    print("f:",x)
    except StopIteration as e:
    print("生成器最后的返回值是:",e.value)
    break

    第二类:生成器表达式:类似于列表推导,只不过是把一对大括号[]变换为一对小括号()。但是,生成器表达式是按需产生一个生成器结果对象,要想拿到每一个元素,就需要循环遍历。

    如下案例加以说明:

    # 一个列表
    xiaoke=[2,3,4,5]
    # 生成器generator,类似于list,但是是把[]改为()
    gen=(a for a in xiaoke)
    for i in gen:
    print(i)
    #结果是:

    # 为什么要使用生成器?因为效率。
    # 使用生成器表达式取代列表推导式可以同时节省 cpu 和 内存(RAM)。
    # 如果你构造一个列表(list)的目的仅仅是传递给别的函数,
    # 比如 传递给tuple()或者set(), 那就用生成器表达式替代吧!

    # 本案例是直接把列表转化为元组
    kk=tuple(a for a in xiaoke)
    print(kk)
    #结果是:
    (2, 3, 4, 5)

    # python内置的一些函数,可以识别这是生成器表达式,外面有一对小括号,就是生成器
    result1=sum(a for a in range(3))
    print(result1)
    # 列表推导式
    result2=sum([a for a in range(3)])
    print(result2)
    ————————————————
    版权声明:本文为CSDN博主「ITxiaoke」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/u014745194/article/details/70176117

     
     
     
  • 相关阅读:
    jsp和servlet有什么区别?
    JavaScript中null、undefined有什么区别?
    Java中的信号量Semaphore
    Java中实现线程通信方式有哪些?
    说说对于sychronized同步锁的理解
    线程的run()方法和start()方法有什么区别?
    高并发大型网站架构设计
    高并发高流量网站架构(转)
    构建高并发高可用的电商平台架构实践
    自己搭建CDN服务器静态内容加速-LuManager CDN使用教程
  • 原文地址:https://www.cnblogs.com/sddai/p/12129880.html
Copyright © 2011-2022 走看看