zoukankan      html  css  js  c++  java
  • python_魔法方法(六):迭代器和生成器

    迭代器

    自始至终,都有一个概念一直在用,但是我们却没来都没有人在的深入剖析它。这个概念就是迭代。

    迭代的意思有点类似循环,每一次的重复的过程被称为迭代的过程,而每一次迭代得到的结果会被用来作为下一次迭代的初始值。提供迭代方法的容器称为迭代器,通常接触的迭代器有序列(列表、元组、字符串)还有字典也是迭代器,都支持迭代的操作。举个列子,通常使用for循环进行迭代。

    >>> for i in "python":
        print(i)
    
    打印结果    
    p
    y
    t
    h
    o
    n

    字符串就是一个容器,同时也是一个迭代器,for语句的作用就是触发这个迭代器的迭代功能,每次从容器里面依次拿出一个数据就,这就是迭代操作。

    字典和文件也是支持迭代操作的:

    >>> list1 = {"python":"python3.x",
         "go":"go是一种语言语法",
         "int":"python关键字",
         }
    >>> for i in list1:
        print("%s --> %s"%(i,list1[j]))
    
    >>> for i in list1:
        print("%s --> %s"%(i,list1[i]))

    打印结果;

    python --> python3.x
    go --> go是一种语言语法
    int --> python关键字

    关于迭代,python还提供了两个BIF:iter()  next()

    对一个容器对象调用iter()就得到它的迭代器,调用next()迭代器就会返回下一个值,然后怎么结束了?如果迭代器没有值可以返回,python将抛出一个叫做Stoplteration的异常。

    >>> string = "python"
    >>> it = iter(string)
    >>> next(it)
    'p'
    >>> next(it)
    'y'
    >>> next(it)
    't'
    >>> next(it)
    'h'
    >>> next(it)
    'o'
    >>> next(it)
    'n'
    >>> next(it)
    Traceback (most recent call last):
      File "<pyshell#26>", line 1, in <module>
        next(it)
    StopIteration

    所以,利用这两个BIF,可以分析出for语句其实就是这么工作的:

    >>> string = "python"
    >>> it = iter(string)
    >>> while True:
        try:
            each = next(it)
        except StopIteration:
            break
        print(each)
    
        
    p
    y
    t
    h
    o
    n

    那么实现迭代器的魔法方法就有两个:__iter__()   __next__()

    一个容器如果是迭代器,就必须实现__iter__()方法,这个方法实际上就是返回迭代器本身。接下来就是重点实现的是__next__()魔法方法,因为他决定了迭代器的规则,看一段例子:

    >>> class Fibs():
        def __init__(self):
            self.a = 0
            self.b = 1
        def __iter__(self):
            return self
        def __next__(self):
            self.a,self.b = self.b,self.a+self.b
            return self.a
    
        
    >>> fibs = Fibs()
    >>> for i in fibs:
        if i < 20:
            print(i)
        else:
            break
    
        
    1
    1
    2
    3
    5
    8
    13

    这个迭代器的唯一亮点就是没有终点,所以如果没有跳出循环,它会不断迭代下去。那么就可以加一个参数,用于控制迭代的范围。

    >>> class Fibs():
        def __init__(self,n = 20):
            self.a = 0
            self.b = 1
            self.n = n
        def __iter__(self):
            return self
        def __next__(self):
            self.a,self.b = self.b,self.a+self.b
            if self.a > self.n:
                raise StopIteration
            return self.a
    
    >>> fibs = Fibs()
    >>> for i in fibs:
        print(i)
    
        
    1
    1
    2
    3
    5
    8
    13

    生成器

    前面是迭代器,下面来说生成器

    生成器其实就是迭代器的一种实现,那既然迭代器就可以实现,为何还要生成器了?生成器之所以存在就是为了是python更为简洁,因为迭代器需要我们自己去定义一个类和实现相关的方法,而生成器则需要在普通函数中加一个yield语句即可。

    生成器的发明,使python模仿协同程序的概念得以实现,所谓协同程序,就是可以运行的独立函数调用,函数可以暂停或者挂起,并需要的时候从程序离开的地方继续或重新开始。

    对于调用一个普通的python函数,一般是从函数的第一行代码开始执行,结束与return语句,异常或函数所有语句执行完毕,一但函数将控制权交给调用者,就意味着全部结束。函数中做的所有工作以及保存在局部变量中的数据将丢失。再次调用这个函数时一切都将从头创建。

    python是通过生成器来实现类似于协同程序的概念:生成器可以暂时挂起函数,并保留函数的局部变量等数据,然后在再次调用他的时候,从上次暂停的位置继续执行下去。

    举个例子:

    >>> def myGen():
        print("生成器被执行!")
        yield 1
        yield 2
    
        
    >>> myG = myGen()
    >>> next(myG)
    生成器被执行!
    1
    >>> next(myG)
    2
    >>> next(myG)
    Traceback (most recent call last):
      File "<pyshell#76>", line 1, in <module>
        next(myG)
    StopIteration

    通过例子,当函数结束的时候,一个StopIteration异常就会被抛出。由于Python的for循环会自动调用next()方法和处理StopIteration异常,所以for循环当然也是可以对生成器产生作用的:

    >>> def myGen():
        print("生成器被执行!")
        yield 1
        yield 2
    
        
    >>> myG = myGen()
    >>> for i in myG:
        print(i)
    
        
    生成器被执行!
    1
    2

    前面说的斐波那契数列例子,也可以用生成器实现:

    >>> def fibs():
        a,b = 0,1
        while True:
            a,b = b,a+b
            yield a
    
            
    >>> for i in fibs():
        if i>100:
            break
        print(i)
    
        
    1
    1
    2
    3
    5
    8
    13
    21
    34
    55
    89

    写到现在,可以很好的用类别推导式了,哈哈,先来看下下面的什么意思吧

    >>> a =  [i for i in range(100) if not (i%2) and i%3]
    >>> print(a)
    [2, 4, 8, 10, 14, 16, 20, 22, 26, 28, 32, 34, 38, 40, 44, 46, 50, 52, 56, 58, 62, 64, 68, 70, 74, 76, 80, 82, 86, 88, 92, 94, 98]

    含义很简单的,打印100以内能被2整除,不能被3整除

    >>> b = {i:i%2==0 for i in range(10)}
    >>> b
    {0: True, 1: False, 2: True, 3: False, 4: True, 5: False, 6: True, 7: False, 8: True, 9: False}

    还有集合推导式:

    >>> c = {i for i in [1,1,2,3,3,4,5,5,5,6,7,7,7,8]}
    >>> c
    {1, 2, 3, 4, 5, 6, 7, 8}

    那么有没有字符串推导式,来验证下

    >>> d = "I love python!"
    >>> d
    'I love python!'

    看吧,不对吧,打印的是整个包含在字符串中的,所以不存在字符串推导式的说法,那元组了?

    >>> e = (i for i in range(10))
    >>> e
    <generator object <genexpr> at 0x0000000003031360>

    看打印出来的信息是generator,这就是生成器,用小括号括起来的正是生成器,来看下效果

    >>> next(e)
    0
    >>> next(e)
    1
    >>> next(e)
    2
    >>> next(e)
    3
    >>> next(e)
    4
    >>> next(e)
    5
    >>> next(e)
    6
    >>> next(e)
    7

    用for循环吧剩下的打印出来

    >>> for i in e:
        print(i)
    
        
    8
    9

    生成器推导式如果作为函数的参数,可以直接写推导式,而不用加小括号(这个很牛气吧)。

    >>> sum(i for i in range(100) if i%2)
    2500
  • 相关阅读:
    结构struct 联合Union和枚举Enum的细节讨论
    ubuntu 查询 修改 时间
    在Ubuntu上下载、编译和安装Android最新内核源代码(Linux Kernel)
    浅谈Android系统开发中LOG的使用
    如何单独编译Android源代码中的模块
    在Ubuntu为Android硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口
    在Ubuntu上为Android系统编写Linux内核驱动程序
    在Ubuntu上为Android系统内置Java应用程序测试Application Frameworks层的硬件服务
    在Ubuntu上为Android系统内置C可执行程序测试Linux内核驱动程序
    在Ubuntu上为Android增加硬件抽象层(HAL)模块访问Linux内核驱动程序
  • 原文地址:https://www.cnblogs.com/pinpin/p/9926366.html
Copyright © 2011-2022 走看看