• 【Python之路】特别篇--生成器(constructor)、迭代器(iterator)、可迭代对象(iterable)


    生成器(constructor)

      生成器函数在Python中与迭代器协议的概念联系在一起。包含yield语句的函数会被特地编译成生成器 !!!

      当函数被调用时,他们返回一个生成器对象,这个对象支持迭代器接口。

      不像一般的函数会生成值后退出,生成器函数在生成值后会自动挂起并暂停他们的执行和状态,他的本地变量将保存状态信息,这些信息在函数恢复时将再度有效

    创建生成器方式有两种:

    方法一:

    s = ( x for x in range(5) )

    方法二:

    def foo():
        print('OK')
        yield 1
    

    例子:

    def g(n):
        for i in range(n):
            yield i **2
    
    for i in g(5):
        print(i)
    

    要了解他的运行原理,我们来用next方法看看:

    t = g(5)
    print(t.__next__())      # 0 
    print(t.__next__())      # 1 
    print(t.__next__())      # 4 
    print(t.__next__())      # 9 
    print(t.__next__())      # 16 
    print(t.__next__())
    
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration

    在运行完5次next之后,生成器抛出了一个StopIteration异常,迭代终止。

    send(msg) 与 next()

    了解了next()如何让包含yield的函数执行后,我们再来看另外一个非常重要的函数send(msg)。

    其实next()和send()在一定意义上作用是相似的,区别是send()可以传递yield表达式的值进去,而next()不能传递特定的值,只能传递None进去。因此,我们可以看做

    c.next() 和 c.send(None) 作用是一样的。

    def g(n):
        for i in range(n):
            ret = yield i **2
            print(ret)
    
    
    t = g(5)
    print(t.__next__())
    print(t.send('Hello'))
    
    # 0 Hello 1
    

    需要注意的是,第一次调用时,请使用next()语句或是send(None),不能使用send发送一个非None的值,否则会出错的,因为没有yield语句来接收这个值。

    send(msg) 和 next()是有返回值的,它们的返回值很特殊,返回的是yield表达式的参数 !

    执行顺序是:遇到yield 先返回值,等下次再进入时再用 msg 进行赋值!!!

    再来看一个yield的例子,用生成器生成一个Fibonacci数列:

    def fab(max):
        a, b = 0, 1
        while a < max:
            yield a
            a, b = b, a + b
    
    for i in fab(20):
        print(i)
    
    # 0 1 1 2 3 5 8 13
    

    另一个 yield 的例子来源于文件读取。

     def read_file(fpath): 
        BLOCK_SIZE = 1024 
        with open(fpath, 'rb') as f: 
            while True: 
                block = f.read(BLOCK_SIZE) 
                if block: 
                    yield block 
                else: 
                    return
    

      

    迭代器(iterator)

      for循环可以用于Python中的任何类型,包括列表、元组等等,实际上,for循环可用于任何“可迭代对象”!

      迭代器是一个实现了迭代器协议的对象,Python中的迭代器协议就是有next方法的对象会前进到下一结果,而在一系列结果的末尾是,则会引发StopIteration。

      任何这类的对象在Python中都可以用for循环或其他遍历工具迭代,迭代工具内部会在每次迭代时调用next方法,并且捕捉StopIteration异常来确定何时离开。

      使用迭代器一个显而易见的好处就是:每次只从对象中读取一条数据,不会造成内存的过大开销。

    注意

      判断迭代器的条件是:

    1. 有__iter__ 方法

    2. 有__next__ 方法

      所有的生成器都是迭代器!

    迭代器例子:

    比如要逐行读取一个文件的内容,利用readlines()方法,我们可以这么写:

    for line in open("test.txt").readlines():
        print (line)
    

    这样虽然可以工作,但不是最好的方法。因为他实际上是把文件一次加载到内存中,然后逐行打印。当文件很大时,这个方法的内存开销就很大了。

    利用file的迭代器,我们可以这样写:

    for line in open("test.txt"): 
        print (line)
    

    这是最简单也是运行速度最快的写法,他并没显式的读取文件,而是利用迭代器每次读取下一行。

    for 循环实质

    1. 调用 __iter__ 方法将可迭代对象转换成迭代器

    2. 对迭代器对象不断调用 __next__ 方法

    3. 处理StopIteration 异常

    class Fab(object): 
        def __init__(self, max): 
            self.max = max 
            self.n, self.a, self.b = 0, 0, 1 
    
        def __iter__(self): 
            return self 
    
        def __next__(self): 
            if self.n < self.max: 
                r = self.b 
                self.a, self.b = self.b, self.a + self.b 
                self.n = self.n + 1 
                return r 
            raise StopIteration()
    
    
    '''
    >>> for key in Fabs(5):
        print key
     
         
    1
    1
    2
    3
    5
    '''
    View Code

    可迭代对象(iterable)

      判断条件:内部具有 __iter__ 方法

    from collections import Iterable,Iterator
    print(isinstance(range(1),Iterable))
    print(isinstance(range(1),Iterator))
    
    print(isinstance(list(),Iterable))
    print(isinstance(list(),Iterator))
    

      

  • 相关阅读:
    查找数据库表中重复的 Image 类型值
    C#中的引用传递和值传递。
    用JS解决Asp.net Mvc返回JsonResult中DateTime类型数据格式的问题
    根据业务自己设计的.NET工厂模式架构
    封装EF code first用存储过程的分页方法
    2013款MacBook Air装Windows7单系统
    js判断是否在微信浏览器中打开
    EF Code First连接现有数据库
    JS中for循序中延迟加载实现动态效果
    DIV+CSS左右两列自适应高度的方法
  • 原文地址:https://www.cnblogs.com/5poi/p/6785785.html
Copyright © 2020-2023  润新知