Python基础8——迭代器 生成器

1.迭代器

  • 定义:帮助程序员逐一获取某种对象中的元素

    • 对象:包含str、list、tuple、dict、set等序列,即可迭代对象
    • 表象:具有__next__方法且每次调用都获取可迭代对象中的元素(从前到后逐次获取)
    • 判别一个对象是迭代器的标准:内部是否有__next__方法
  • 列表转换成迭代器

    • 方法1:列表作为参数传给内置函数iter()
    • 方法2:列表调用本身的转换方法__iter__()
    v1 = [11, 22, 33, 44]
    # 列表转换成迭代器
    v2 = iter(v1)
    v3 = v1.__iter__()
    print(v2, type(v2))
    print(v3, type(v3))
    # <list_iterator object at 0x14204f040> <class 'list_iterator'>
    # <list_iterator object at 0x100a9a800> <class 'list_iterator'>
    
    
  • 读取迭代器内容

    • 方法:反复调用 __next__()方法,直到报错StopIteration错误,表示已经迭代完毕
    result1 = v2.__next__()
    print(result1)
    result2 = v2.__next__()
    print(result2)
    result3 = v2.__next__()
    print(result3)
    result4 = v2.__next__()
    print(result4)
    result5 = v2.__next__()
    print(result5)
    
    v1 = (11, 22, 33, 44)  # v1 = {'k1': 'v1', 'k2': 'v2'}  v1 = "hello world" 均可
    v2 = iter(v1)
    while True:
        try:
            val = v2.__next__()
            print(val)
        except Exception as e:
            break
    
  • for循环隐含步骤

    • 1.内部会将列表等可迭代对象转换成迭代器
    • 2.内部调用__next__()反复执行迭代器.
    • 3.取完不报错
    for item in [11, 22, 33]:
        print(item)
    

2.可迭代对象

  • 判断标准
    • 内部具有__iter__()方法且返回一个迭代器(*)
    • 可以被for循环
    v1 = [11, 22, 33, 44]
    ret = v1.__iter__()
    print(ret)
    

3.生成器【函数的变异】

  • 判断标准

    • 函数中如果存在yield,该函数就是一个生成器函数,调用生成器函数会返回一个生成器
    • 生成器只有被for循环时,生成器函数内部的代码才会执行,每次循环都会获取yield返回的值
  • 案例分析

    • 1.普通函数
    def func():
      return 123
    func()  # 执行函数,函数内部代码就会运行,结束后返回123
    
    • 2.简单生成器函数【内部是否包含yield】
    def func():
        print('f1')
        yield 1
        print('f2')
        yield 2
        print('f3')
        yield 100
        print('f4')
    
    # 函数的内部代码不会执行,返回一个生成器对象
    v1 = func()
    
    # 生成器可以被for循环,一旦开始循环,那么函数内部代码就会开始执行
    for item in v1:
        print(item)
    # f1  第1次循环,执行函数,打印
    # 1   然后返回1
    # f2  第2次循环,接着上次yield后的位置继续执行,打印
    # 2  然后返回2
    # f3  第3次循环,接着上次yield后的位置继续执行,打印
    # 100  然后返回100
    # f4  第4次循环,接着上次yield后的位置继续执行,打印
    
    count = 0
    for item in v1:  # v1是上一段代码中返回的生成器对象
        print(item)
        count += 1
        if count == 2:
            break
    # f1
    # 1
    # f2
    # 2
    
    def func():
        count = 1
        while True:
            yield count
            count += 1
    
    val = func()
    # for item in val:
        # print(item)  # 死循环
    
    def func():
        return 1
        if 1 != 1:
            yield 1
            yield 9
    
    v = func()
    print(v, type(v))  # <generator object func at 0x141a36140> <class 'generator'>
    # 只要函数中有yield就是生成器,不用管yield语句能否执行
    
    def func():
        return 1
        yield 1
        yield 9
    
    v = func()
    for item in v:
        print(item) # 没有任何输出
    
    def func():
        yield 9
        return 111
        yield 10
    
    v = func()
    for item in v:
        print(item) # 输出9
    
    def func():
        count = 1
        while True:
            yield count
            count += 1
            if count == 100:
                return
    
    val = func()
    for item in val:
        print(item, " ", end="") # 顺序打印1-99
    
    • 3.以读文件模仿读取redis示例
    # 代码1
    def func():
        while True:
            f = open('./db', mode='r', encoding='utf-8') # 通过网络连接上redis
            data = f.readline()
            f.close() # 关闭与redis的连接
            yield data
    
    # 代码2 实现了按行读取数据,但链接会一直连着占用资源,导致其他人无法连接redis
    def func():
        f = open('./db', mode='r', encoding='utf-8')  # 通过网络连接上redis
        while True:
            data = f.readline()
            if not data:
                break
            yield data
        f.close() # 关闭与redis的连接
    
    # 代码3:模仿redis源码实现
    def func():
        cursor = 0
        while True:
            f = open('./db', mode='r', encoding='utf-8')  # 通过网络连接上redis
            # 代指 redis[0:10]
            f.seek(cursor)
            data_list = []
            for i in range(10):
                line = f.readline()
                if not line:
                    return
                data_list.append(line)
            cursor = f.tell()
            f.close()  # 关闭与redis的连接
    
            for row in data_list:
                yield row
    
    for item in func():
        print(item)
    
    # redis源码示例
    def scan_iter(
        self,
        match: Union[PatternT, None] = None,
        count: Union[int, None] = None,
        _type: Union[str, None] = None,
        **kwargs,
    ) -> Iterator:
        """
        Make an iterator using the SCAN command so that the client doesn't
        need to remember the cursor position.
    
        ``match`` allows for filtering the keys by pattern
    
        ``count`` provides a hint to Redis about the number of keys to
            return per batch.
    
        ``_type`` filters the returned values by a particular Redis type.
            Stock Redis instances allow for the following types:
            HASH, LIST, SET, STREAM, STRING, ZSET
            Additionally, Redis modules can expose other types as well.
        """
        cursor = "0"
        while cursor != 0:
            # 每次去取100个
            # cursor是取完之后的游标位置
            # data本次取出来100条数据
            cursor, data = self.scan(
                cursor=cursor, match=match, count=count, _type=_type, **kwargs
            )
            yield from data
    
    # redis读取数据示例.py
    import redis
    
    conn = redis.Redis(host='192.168.11.11')
    # scan_iter是一个生成器函数
    result = conn.scan_iter(count=100)
    for item in result:
        print(item)
    
    conn.close()
    

    • 4.带yield from的生成器
    def base():
        yield 88
        yield 99
    
    def func():
        yield 1
        yield 2
        yield from base() # 在函数和函数之间交叉着做生成器
        yield 3
    
    result = func()
    for item in result:
        print(item)
    # 1
    # 2
    # 88
    # 99
    # 3
    
    def base():
        # return 123 执行报错 123不是可迭代对象
        return [11, 22] # 将列表拆分返回
        
    
    def func():
        yield 1
        yield 2
        yield from base()
        yield 3
    
    result = func()
    for item in result:
        print(item)
    # 1
    # 2
    # 11
    # 22
    # 3
    
    def base():
        return [11, 22] # 将列表整体返回,意义不大
        
    def func():
        yield 1
        yield 2
        yield base()
        yield 3
    
    result = func()
    for item in result:
        print(item)
     # 1
     # 2
     # [11, 22]
     # 3
    
    • 5.生成器推导式
      • 面试题:请比较 [i for i in range(10)] 和 (i for i in range(10)) 的区别?
        • 前者直接生成列表,后者是一个生成器(注意后者不是集合)
    // 1.复习列表推导式
    # def func():
    #     result = []
    #     for i in range(10):
    #         result.append(i)
    #     return result
    # v1 = func()
    v1 = [i for i in range(10)] # 列表推导式,立即循环创建所有元素
    print(v1)
    
    v1 = [lambda :i for i in range(10)]
    for item in v1:
        print(item(), ' ', end="")
    # 9  9  9  9  9  9  9  9  9  9  
    
    def func():
        result = []
        for i in range(10):
            def f():
                return i
            result.append(f)
        return result
    v1 = func()
    for item in v1:
        print(item(), ' ', end="")  # 9  9  9  9  9  9  9  9  9  9 
    
    // 2.生成器推导式案例
    # def func():
    #     for i in range(10):
    #         yield i
    v2 = (i for i in range(10)) # 生成器推导式,创建了一个生成器,内部循环未执行
    print(v2)
    for item in v2:
        print(item, ' ', end="")
    # <generator object <genexpr> at 0x13a101840>
    # 0  1  2  3  4  5  6  7  8  9  
    
    def func():
        for i in range(10):
            def f():
                return i
            yield f
    v1 = func()
    for item in v1:
        print(item(), ' ', end="")  # 0  1  2  3  4  5  6  7  8  9
    
    v1 = (lambda :i for i in range(10))
    for item in v1:
        print(item(), ' ', end="")  # 0  1  2  3  4  5  6  7  8  9
    

4.小结

  • 迭代器
    • 对可迭代对象中的元素进行逐一获取,迭代器内部都有一个__next__()方法,用于逐个获取数据
  • 可迭代对象
    • 可以被for循环
    • 此类对象中有__iter__()方法且要返回一个迭代器或生成器
  • 生成器
    • 函数内部有yield则就是生成器函数,调用函数则返回一个生成器,循环生成器时则函数内部代码才会执行
    • 是一种特殊的迭代器,也是一种特殊的可迭代对象
    def func():
        yield 1
    
    v = func()
    print(v, type(v))
    
    for i in dir(v):
        print(i) # 查看v中都有哪些方法 出现了__iter__() 和 __next__()
    
    def func():
        yield 1
    
    v = func()
    ret = v.__iter__()
    print(ret)  # <generator object func at 0x134b4c510>
    
    class Foo(object):
      pass
    
    # 创建类的对象
    obj = Foo()
    
    # 不能被循环,不是可迭代对象
    for item in obj:
        print(item)  # 报错TypeError: 'Foo' object is not iterable
    
    class Foo(object):
        def __iter__(self):
            return iter([11, 22, 33])
    
    # 创建类的可迭代对象
    obj = Foo()
    
    # 可以被for循环
    for item in obj:
        print(item) # 11  22  33
    
    class Foo(object):
        def __iter__(self):
            yield 1
            yield 2
            yield 3
    
    # 创建类的可迭代对象
    obj = Foo()
    
    # 可以被for循环
    for item in obj:
        print(item)  # 1  2  3 
    
posted @ 2025-02-08 10:23  pycoder_666  阅读(27)  评论(0)    收藏  举报