• 【人生苦短,我学 Python】(15)迭代器、生成器



    Python 所有文章传送门
    【Python】所有文章传送门

    简述 / 前言

    大多数容器对象都可以使用 for 语句。而迭代器的使用非常普遍,并使得 Python 成为一个统一的整体。在幕后,for 语句会在容器对象上调用 iter()。 该函数返回一个定义了 __next__() 方法的迭代器对象,此方法将逐一访问容器中的元素。 当元素用尽时,__next__() 将引发 StopIteration 异常来通知终止 for 循环。

    1. 迭代器

    实现了 __next()__ 的对象是迭代器,可以使用内置函数 next(),调用迭代器的 __next__() 方法,依次返回下一个项目值。

    迭代器对象必须实现两个方法:__iter__()__next()____iter__() 用于返回对象本身,__next()__ 用于返回下一元素。

    比如下述代码:

    >>> s = 'abcde'
    >>> it = iter(s)
    >>> next(it)
    'a'
    >>> next(it)
    'b'
    >>> next(it)
    'c'
    >>> next(it)
    'd'
    >>> next(it)
    'e'
    >>> next(it)
    Traceback (most recent call last):
      File "", line 1, in <module>
    StopIteration
    

    看一个官方写的例子:

    class Reverse:
        """Iterator for looping over a sequence backwards."""
        def __init__(self, data):
            self.data = data
            self.index = len(data)
    
        def __iter__(self):
            return self
    
        def __next__(self):
            if self.index == 0:
                raise StopIteration
            self.index = self.index - 1
            return self.data[self.index]
    
    rev = Reverse('spam')
    iter(rev)
    for char in rev:
        print(char)
    

    其输出如下:

    m
    a
    p
    s
    

    比如我们再实现一个由迭代器生成的不大于100的斐波拉契数列:

    class Fib:
        def __init__(self):
            self.l, self.r = 0, 1
    
        def __iter__(self):
            return self
    
        def __next__(self):
            self.l, self.r = self.r, self.l + self.r
            return self.l   # f(n) = f(n-1) + f(n-2)
    
    fibs = Fib()
    for f in fibs:
        if f <= 100:
            print(f, end=', ')
        else:
            break
    

    其输出如下:

    1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 
    

    2. 生成器

    生成器函数使用 yield 语句返回一个值,然后保存当前函数整个执行状态,等待下一次调用。每次在生成器上调用 next() 时,它会从上次离开的位置恢复执行(它会记住上次执行语句时的所有数据值)。除了会自动创建方法和保存程序状态,当生成器终结时,它们还会自动引发 StopIteration

    看一个官方的例子:

    def reverse(data):
        for index in range(len(data)-1, -1, -1):
            yield data[index]
    
    for char in reverse('spam'):
        print(char)
    

    其输出如下:

    m
    a
    p
    s
    

    第二个例子是生成数字0~9,用于自增函数,代码如下:

    # !/usr/bin/env python3
    # _*_ coding: utf-8 _*_
    
    def CSDN(n):
        for i in range(n):
            yield i
    
    # 方法一
    for t in CSDN(10):
        print(t, end=', ')
    
    # 方法二
    print()
    f = CSDN(10)
    it = iter(f)
    for i in range(10):
        print(next(it), end=', ')
    

    其输出如下:

    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 
    

    又比如利用生成器输出不大于100的斐波拉契数列:

    def Fib():
        a, b = 0, 1
        while 1:
            a, b = b, a + b
            yield a  # f(n)=f(n-1)+f(n-2)
    
    fibs = Fib()
    for f in fibs:
        if f <= 100:
            print(f, end=', ')
        else:
            break
    

    其输出如下:

    1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 
    

    2.1 生成器表达式

    与其相关的一个是生成器表达式。某些简单的生成器可以写成简洁的表达式代码,所用语法类似列表推导式,但外层为圆括号而非方括号。

    比如:

    >>> sum(i*i for i in range(10))
    285
    >>> data = 'spam'
    >>> list(data[i] for i in range(len(data)-1, -1, -1))
    ['m', 'a', 'p', 's']
    

    3. 常用的迭代器

    3.1 enumerate 迭代器

    这是一个可用于枚举可迭代对象中元素的迭代器,它会以 (计数, 元素) 的形式返回:

    celebrities = ['陈凯歌', '徐克', '林超贤', '吴京#伍千里', '易烊千玺#伍万里', '段奕宏#谈子为', '朱亚文#梅生', '李晨#余从戎', '胡军#雷公', '韩东君#平河', '张涵予#宋时轮', '黄轩#毛岸英']
    len_c = len(celebrities)
    for num, name in enumerate(celebrities):
        print(f'{num+1}/{len_c}: {name}')
    

    其输出如下:

    1/12: 陈凯歌
    2/12: 徐克
    3/12: 林超贤
    4/12: 吴京#伍千里
    5/12: 易烊千玺#伍万里
    6/12: 段奕宏#谈子为
    7/12: 朱亚文#梅生
    8/12: 李晨#余从戎
    9/12: 胡军#雷公
    10/12: 韩东君#平河
    11/12: 张涵予#宋时轮
    12/12: 黄轩#毛岸英
    

    3.2 filter 迭代器

    filter 是可迭代对象,使用指定函数处理可迭代对象的每个元素,函数返回 bool 类型的值。若结果为 True,则返回该元素。如果结果为 None,则返回元素为 True 的元素。

    这个迭代器可以用来过滤特定值,比如去除下列列表中的空值:

    • 1 删除空内容(方法一)

      celebrities = ['', '陈凯歌', '', '徐克', '林超贤', '', '', '吴京#伍千里', '易烊千玺#伍万里', '段奕宏#谈子为', '', '朱亚文#梅生', '', '李晨#余从戎', '胡军#雷公', '韩东君#平河', '', '', '张涵予#宋时轮', '', '黄轩#毛岸英']
      
      print(list(filter(None, celebrities)))
      

      输出:

      ['陈凯歌', '徐克', '林超贤', '吴京#伍千里', '易烊千玺#伍万里', '段奕宏#谈子为', '朱亚文#梅生', '李晨#余从戎', '胡军#雷公', '韩东君#平河', '张涵予#宋时轮', '黄轩#毛岸英']
      
    • 2 删除空内容(方法二)
      需要配合 lambda 表达式一起使用!

      celebrities = ['', '陈凯歌', '', '徐克', '林超贤', '', '', '吴京#伍千里', '易烊千玺#伍万里', '段奕宏#谈子为', '', '朱亚文#梅生', '', '李晨#余从戎', '胡军#雷公', '韩东君#平河', '', '', '张涵予#宋时轮', '', '黄轩#毛岸英']
      
      print(list(filter(lambda x: x != '', celebrities)))
      

      输出:

      ['陈凯歌', '徐克', '林超贤', '吴京#伍千里', '易烊千玺#伍万里', '段奕宏#谈子为', '朱亚文#梅生', '李晨#余从戎', '胡军#雷公', '韩东君#平河', '张涵予#宋时轮', '黄轩#毛岸英']
      

    具体有关的删除操作可见我之前写的一篇文章:python 一次性删除列表(list)的空白元素(空内容) 或者 一次性删除列表(list)中的指定元素 - 小邓在森林

    3.3 map 迭代器

    使用指定函数处理可迭代对象的每个元素。示例代码如下:

    >>> list(map(abs, [-1, 100, 0, -56]))
    [1, 100, 0, 56]
    

    3.4 zip 迭代器

    它可以拼接多个可迭代对象的元素。示例代码如下:

    >>> list(zip([1, 2, 3], 'abc', range(3)))
    [(1, 'a', 0), (2, 'b', 1), (3, 'c', 2)]
    >>> list(zip([1, 2, 3], 'abc', range(30)))		# 多出的元素不会被拼接
    [(1, 'a', 0), (2, 'b', 1), (3, 'c', 2)]
    
  • 相关阅读:
    海外推广运营的技巧汇总
    java使用selenium自动化WebDriver等待
    算法-动态规划/trie树-单词拆分
    盘点JDK中基于CAS实现的原子类
    webpack插件plugin 添加版权 打包html js压缩
    【Java-----IO流(三)之缓冲流详解】
    webservice原始调用(无论是java的webservice还是.net或是php的都可用)
    Jmeter--简单的快捷键设置
    建筑模板常见的问题有哪些?
    jQuery基础----绑定和解绑事件的方法
  • 原文地址:https://blog.csdn.net/senlin_6688/article/details/140361924