zoukankan      html  css  js  c++  java
  • Python之容器、迭代器、生成器

    1.容器、可迭代对象、迭代器、生成器概念

    1. 容器:存储许多元素的数据结构。通常存储在内存(迭代器、生成器是特例)可以使用in来判断某个元素是否在存在的对象都是容器

    举个例子:容器就像一个箱子,里面可以存放许多东西,我可以往这个箱子存取东西,可以判断这个箱子是否有某样东西

    2.可迭代对象:可以使用iter()变成迭代器的对象都是可迭代对象,大部分容器都是可迭代对象(str,set,list,tuple,打开状态的files,sockets等等)

    3.迭代器:它是一个带状态的对象,保存当前记录状态,当使用next()函数调用时,可以返回容器的下一个值。如果容器中没有更多元素了,则抛出StopIteration异常.

    任何实现了__iter__() and next()方法的对象都是迭代器,__iter__返回迭代器自身,__next__对象返回下一个值。生成器是另一种形态的迭代器

    迭代器就是实现了工厂模式的对象,它在你每次你询问要下一个值的时候给你返回。比如itertools函数返回的都是迭代器对象。

    4.生成器:使用yield关键字,python都会把这个函数视为一个生成器.它返回了一个可迭代的对象,可以使用next()访问它里面的值了

    5.如何使用迭代器呢?

    1.方式是使用for 。。 in 。语法糖结构来遍历迭代器。
    for默认的会调用__iter__返回一个迭代对象,然后调用next()方法调用迭代对象的__next__方法获取下一个值。
    如果最后没有值了,迭代器会抛出一个异常,for会捕捉处理这个异常,退出循环.

    2.先获取迭代器对象,如使用it = iter(list)方法,在调用next(it)获取下一个值。最后没值了会抛出异常(StopIteration)

    下面一张图来说明他们之间的关系

    image

    2.具体实例来演示上面的内容

    以斐波那契数列来举例

    1.普通的函数实现

    # 普通函数斐波那契数列
    def fib1(n):
        pre, cur = 0, 1
        i = 0
        while i < n:
            print(cur, end=' ')
            pre, cur = cur, cur + pre
            i += 1
    
    调用: fib1(10)
    输出:1 1 2 3 5 8 13 21 34 55
    

    2.普通的函数实现改进

    # 普通函数斐波那契数列改进
    def fib1(n):
        result = []
        pre, cur = 0, 1
        i = 0
        while i < n:
            result.append(cur)
            pre, cur = cur, cur + pre
            i += 1
        return result
        
    调用:lst = fib1(10)
          print(lst)
    输出:[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
    

    3.自定义一个可迭代的类来实现斐波那契数列

    class Fib:
    
        def __init__(self, stop):
            self.stop = stop
            self.pre = 0
            self.cur = 1
            self.start = 0
            print('init finish')
    
        def __iter__(self):
            print('return iter')
            return self
    
        def __next__(self):
            print('return next value')
            if self.start < self.stop:
                self.start += 1
                x = self.cur
                self.pre, self.cur = self.cur, self.cur + self.pre
                return x
            else:
                raise StopIteration()
    
    初始化类,得到一个类实例对象f,可以看到"init finish"打印出来了
    In [1]: f = Fib(10)
    Out[1]:init finish
    
    我们知道一个可迭代对象可以通过iter()函数转换成一个迭代器,
    从下面的调用我们可以看出,当我们使用iter()函数时,它默认去调用函数的__iter__()函数,返回迭代对象
    In [2]: it = iter(f)
    Out[2]:return iter
    
    当我们获取了迭代对象,我们调用next()函数来获取我们想要的值,
    那么next()有做了哪些动作呢
    1.它调用类中的__next__()方法
    2.我们__next__()方法,返回当前值,并保存当前值记录状态和算出下一次调用要返回的值
    In [3]: next(it)
    Out[30]:return next value
    Out[30]: 1
    
    In [3]: next(it)
    Out[30]:return next value
    Out[30]: 2
    
    如果一直执行next(),它会一直返回值,直到抛出StopIteration()异常
    
    

    4.使用生成器

    使用生成器,我们有两种形式:
    1.创建生成器函数,使用yield关键字
    2.使用生成器表达式
    

    4.1创建生成器函数

    # 使用yield实现斐波那契数列
    def fib(n):
        pre, cur = 0, 1
        i = 0
        while i < n:
            print('call this')
            yield cur
            pre, cur = cur, pre + cur
            i += 1
            print('here')
    
    从下面的执行结果我们可以看出,使用关键字yield后,调用fib(10)它的返回值是一个生成器
    In [33]: f = fib(10)
    In [34]: type(f)
    Out[34]: generator
    
    从上面图我们知道generator is iterator,我们可以使用next()函数获取值,可以看出我们没执行一次next(),
    好像我们的程序就在yield返回cur,然后停到这不执行,直到下次调用next()函数又继续往下走,不停重复这个过程
    In [1]: next(f)
    call this
    Out[1]: 1
    
    In [2]: next(f)
    here
    call this
    Out[2]: 2
    
    In [3]: next(f)
    here
    call this
    Out[3]: 3
    
    

    4.2生成器表达式实现斐波那契数列

    # 从下面我们可以看出,使用()后a是一个generator类型
    In [1]: a = ( x for x in fib3(10))
    In [2]: type(a)
    Out[2]: generator
    
    In [3]: next(a)
    call this
    Out[3]: 1
    
    In [4]: next(a)
    here
    call this
    Out[4]: 1
    
    In [5]: next(a)
    here
    call this
    Out[5]: 2
    
    

    5.对于可迭代对象遍历,我们一定会想到for循环

    下面具体解释下for循环:

    1.for循环的一般格式如下:
    for iter_var in iterable:
        suite_to_repeat
    从上面可以看出,只要是可迭代对象都可以使用
    它的里面到底做了些什么呢?
    

    举个例子吧,我们看看for循环它到底做了哪些工作

    class Fib:
    
        def __init__(self, stop):
            self.stop = stop
            self.pre = 0
            self.cur = 1
            self.start = 0
            print('init finish')
    
        def __iter__(self):
            print('return iter')
            return self
    
        def __next__(self):
            print('return next value')
            if self.start < self.stop:
                self.start += 1
                x = self.cur
                self.pre, self.cur = self.cur, self.cur + self.pre
                return x
            else:
                raise StopIteration()
               
    对一个可迭代对象进行迭代操作
    def fortest():
        for x in Fib(4):
            print(x)
    
    fortest()
    
    
    import dis
    dis.dis(fortest)
    
    打印以下信息:
    init finish
    return iter
    return next value
    1
    return next value
    1
    return next value
    2
    return next value
    3
    反编译结果
    108           0 SETUP_LOOP              24 (to 26)
                  2 LOAD_GLOBAL              0 (Fib)
                  4 LOAD_CONST               1 (4)
                  6 CALL_FUNCTION            1
                  8 GET_ITER
            >>   10 FOR_ITER                12 (to 24)
                 12 STORE_FAST               0 (x)
    
    109          14 LOAD_GLOBAL              1 (print)
                 16 LOAD_FAST                0 (x)
                 18 CALL_FUNCTION            1
                 20 POP_TOP
                 22 JUMP_ABSOLUTE           10
            >>   24 POP_BLOCK
            >>   26 LOAD_CONST               0 (None)
                 28 RETURN_VALUE
    
    可以看出在for内部有两个很重要的指令GET_ITER,FOR_ITER,
    GET_ITER相当于调用iter(Fib(4),返回对象迭代器it
    FOR_ITER相当于调用next(it),调用类内部的__next()__()方法获取值,一直循环这个过程,直到遇到抛出的StopIteration()异常
    
  • 相关阅读:
    java mail使用qq邮箱发邮件的配置方法
    (利用tempdata判断action是直接被访问还是重定向访问)防止微信活动中用户绕过关注公众号的环节
    判断浏览器为微信浏览器
    解决表单(搜索框)回车的时候直接提交了表单不运行js的问题
    传智播客JavaWeb day11--事务的概念、事务的ACID、数据库锁机制、
    传智播客JavaWeb day10-jdbc操作mysql、连接数据库六大步骤
    页面上常用的一些小功能--QQ、回到顶部
    手机端禁止网页页面放大代码
    Resharp注册码
    NueGet设置package Source
  • 原文地址:https://www.cnblogs.com/lonelyhiker/p/8640647.html
Copyright © 2011-2022 走看看