美文网首页
python学习:迭代器和生成器

python学习:迭代器和生成器

作者: 倔犟的贝壳 | 来源:发表于2021-11-12 23:07 被阅读0次

    在理解迭代器和生成器之前,首先需要了解可迭代对象。我们都知道列表list,字典dict,元组tuple,集合set都是属于容器,都是可迭代的,我们可以通过for obj in 容器去遍历容器内的对象。那是因为所有的容器都有实现一个迭代器,有两个实现两个重要的方法:__iter__和__next__。我们可以通过isinstance(obj, Iterable)来判断一个对象是否可迭代。

    from collections.abc import Iterable
    l = [1,2,3,4] #数组
    t = (1,2,3,4)#元组
    d = {"a":1,"b":2} #字典
    s = {1,3,4,5} #集合
    string = "this is string"
    print(isinstance(l,Iterable)) #True
    print(isinstance(s,Iterable)) #True
    print(isinstance(d,Iterable)) #True
    print(isinstance(s,Iterable)) #True
    

    迭代器

    迭代,是访问集合中的对象元素的一种方法。
    而迭代器是可以帮助记住访问位置的对象。它能从集合的第一个元素开始访问,直到最后一个元素结束。只能向前不能后退。
    如集合set,列表list,字典dict,字符串我们都可以通过for循环迭代。因为它们都是实现了迭代器需要的两个基本方法:__iter__()和__next__().
    我们通过集合set,列表list,字典dict,字符串都可以生成迭代器。

    l = [1,2,3,4]
    it = iter(l) #通过列表生成迭代器
    for x in it: #访问迭代器,依次输出
        print(x,end=" ")
    
    1 2 3 4 
    
    d = {"a":1,"b":2}
    it = iter(d) #字典的迭代器,是根据key值生成。
    for key in it:
        print(key,end=" ") #字典的key值
    
    a b 
    

    迭代器只能向前不能后退,如果在访问之后,我们还继续next,则会抛出一个StopIteration的异常

    l = [1,2,3,4]
    it = iter(l) #通过列表生成迭代器
    for x in it: #访问迭代器,依次输出
        print(x,end=" ")
    
    print("\n迭代完成后,调用next:")
    next(it)
    
    1 2 3 4 
    迭代完成后,调用next:
    
    
    
    ---------------------------------------------------------------------------
    
    StopIteration                             Traceback (most recent call last)
    
    <ipython-input-13-f4f6d96eba47> in <module>
          5 
          6 print("\n迭代完成后,调用next:")
    ----> 7 next(it)
    
    
    StopIteration: 
    

    我们自定义的类,也可以通过实现__iter__和__next__两个方法而可迭代。在生成迭代器的时候和首次开始迭代的时候会调用__iter__()方法,而在每次迭代的时候调用next()方法。

    #定义一个水果类
    class Fruit:
        def __init__(self,type):
            self.type = type
            
     #苹果类       
    class Apple(Fruit):
        def __init__(self):
            super().__init__("Apple")
    #橘子类   
    class Orange(Fruit):
        def __init__(self):
             super().__init__("Orange")
    
    #定义一个水果盒子            
    class FruitBox():
        def __init__(self):
            self.fruit_list = []
            
        def add_fruit(self,fruit):
            self.fruit_list.append(fruit)
    #初始化一个水果盒子
    box = FruitBox()
    #往水果盒子加水果
    box.add_fruit(Apple())
    box.add_fruit(Orange())
    box.add_fruit(Apple())
    
    it = iter(box)
    for fruit in box:
        print("this is :{}".format(fruit.type))
    
    
    
    ---------------------------------------------------------------------------
    
    TypeError                                 Traceback (most recent call last)
    
    <ipython-input-69-b7d2655962a9> in <module>
         27 box.add_fruit(Apple())
         28 
    ---> 29 it = iter(box)
         30 for fruit in box:
         31     print("this is :{}".format(fruit.type))
    
    
    TypeError: 'FruitBox' object is not iterable
    
    #定义一个水果类
    class Fruit:
        def __init__(self,type):
            self.type = type
    
    #定义一个水果盒子            
    class FruitBox():
        def __init__(self):
            self.fruit_list = []
            
        def add_fruit(self,fruit):
            self.fruit_list.append(fruit)
            
        def __iter__(self):
            print("======iter=====")
            self.position = 0
            return self
        def __next__(self):
            print("======next=====")
            if self.position < len(self.fruit_list):
                fruit = self.fruit_list[self.position]
                self.position += 1
                return fruit
            else:
                raise StopIteration
                
    
    #初始化一个水果篮子
    print("start init fruit box")
    box = FruitBox()
    #往水果篮子加水果
    print("start add fruit to box")
    box.add_fruit(Fruit("Apple"))
    box.add_fruit(Fruit("Orange"))
    box.add_fruit(Fruit("Apple"))
    print("start generate a iterator")
    it = iter(box) #生成一个水果篮子的迭代器,然后从通过迭代器从水果篮子取水果
    
    print("loop for iterator to get fruit")
    for fruit in it:
        print("this is :{}".format(fruit.type))
    
    
    
    start init fruit box
    start add fruit to box
    start generate a iterator
    ======iter=====
    loop for iterator to get fruit
    ======iter=====
    ======next=====
    this is :Apple
    ======next=====
    this is :Orange
    ======next=====
    this is :Apple
    ======next=====
    

    生成器

    什么是生成器?在 Python 中,使用了 yield 的函数被称为生成器(generator)。
    它是一个函数,调用生成器函数返回的是一个迭代器。只能用于迭代操作。可以理解为生成器是一个懒惰版的迭代器。只有在调用next()才会生成一个对象。
    在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。

    
     def fibonacci(n): # 生成器函数 - 斐波那契
        a, b, counter = 0, 1, 0
        while True:
            if (counter > n): 
                return
            yield a
            print("after yield")
            a, b = b, a + b
            counter += 1
    f = fibonacci(5) # f 是一个迭代器,由生成器返回生成
    print(f)
    for i in range(5):
        print("start next")
        print(next(f))
        print("end next")
        
    #生成器对象也可以通过list直接转换成列表
    #list(f)
        
    
    <generator object fibonacci at 0x7f977fadc970>
    start next
    0
    end next
    start next
    after yield
    1
    end next
    start next
    after yield
    1
    end next
    start next
    after yield
    2
    end next
    start next
    after yield
    3
    end next
    

    迭代器 vs 生成器

    如果我们要计算从1加到1000000,迭代器需要首先生成把1000000元素先加载到内存中,而生成器只需要在计算的时候生成,拿来用即可,无需为1000000个元素申请内存。生成器在 Python 的写法是用小括号括起来,(i for i in range(1000000)),即初始化了一个生成器。如下:

    import os
    import psutil
    
    # 显示当前 python 程序占用的内存大小
    def show_memory_info(hint):
        pid = os.getpid()
        p = psutil.Process(pid)
        
        info = p.memory_full_info()
        memory = info.uss / 1024. / 1024
        print("{} memory used {} MB".format(hint,memory))
        
        
        
        
    def test_iterator(): 
        show_memory_info("start iterator")
        iterator = [i for i in range(1000000)]
        print(show_memory_info("end iterator"))
        result = sum(iterator)
        print("result is:{}".format(result))
        print(show_memory_info("after call sum"))
    
    def test_generator():
        show_memory_info("start generator")
        generator = (i for i in range(1000000))
        show_memory_info("end generator")
        result = sum(generator)
        print("result is:{}".format(result))
        show_memory_info("after call sum")
        
    def main():
        test_iterator()
        print("=========")
        test_generator()
    
    
    main()
    
    
    start iterator memory used 129.66015625 MB
    end iterator memory used 159.1171875 MB
    None
    result is:499999500000
    after call sum memory used 159.1171875 MB
    None
    =========
    start generator memory used 127.16796875 MB
    end generator memory used 127.16796875 MB
    result is:499999500000
    after call sum memory used 127.16796875 MB
    

    如果水果篮子变成哆啦A梦的口袋,只要想就可以从里面取出水果。那么显然水果篮子已经不够用了,此时需要一个可以无限生成的生成器

    #定义一个水果类
    class Fruit:
        def __init__(self,tag):
            self.tag = tag #每一个都有一个编号
            
    #哆啦A梦的口袋,水果生成器
    def doraemon_pocket(n):
        tag = 0
        while True:
            yield Fruit(tag)
            tag += 1
            
    fruit_generator = doraemon_pocket(0)
    for i in range(10):
        f = next(fruit_generator)
        print("this is number of {} Fruit".format(f.tag))
    
    
    #可以一直取下去
    
    this is number of 0 Fruit
    this is number of 1 Fruit
    this is number of 2 Fruit
    this is number of 3 Fruit
    this is number of 4 Fruit
    this is number of 5 Fruit
    this is number of 6 Fruit
    this is number of 7 Fruit
    this is number of 8 Fruit
    this is number of 9 Fruit
    

    总结:生成器相比迭代器,在无需保存元素的情况下,内存方面是很占优势的。
    yield 返回值在下次next()函数调用的时候才继续执行,这个特性可以很好的利用。

    THE END!

    相关文章

      网友评论

          本文标题:python学习:迭代器和生成器

          本文链接:https://www.haomeiwen.com/subject/wzohzltx.html