【人生苦短,我学 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 "<stdin>", 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)]

相关推荐

  1. 人生 Python】(15生成器

    2024-07-12 19:00:02       20 阅读
  2. 人生 Python】(11)函数(上)

    2024-07-12 19:00:02       49 阅读
  3. 人生python·十》python的异常使用

    2024-07-12 19:00:02       18 阅读
  4. python生成器

    2024-07-12 19:00:02       23 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-12 19:00:02       50 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-12 19:00:02       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-12 19:00:02       43 阅读
  4. Python语言-面向对象

    2024-07-12 19:00:02       54 阅读

热门阅读

  1. hot100 | 十、回溯

    2024-07-12 19:00:02       18 阅读
  2. Eureka: Netflix开源的服务发现框架

    2024-07-12 19:00:02       14 阅读
  3. Gradle 介绍

    2024-07-12 19:00:02       12 阅读
  4. tomcat

    2024-07-12 19:00:02       11 阅读
  5. 【jxls 单元格合并】

    2024-07-12 19:00:02       13 阅读
  6. 基于Hadoop的区块链海量数据存储的设计与实现

    2024-07-12 19:00:02       19 阅读
  7. 1 HTML and CSS

    2024-07-12 19:00:02       16 阅读
  8. 通用脚本大全

    2024-07-12 19:00:02       17 阅读
  9. c#猜数字小游戏

    2024-07-12 19:00:02       19 阅读
  10. TCP/IP模型和OSI模型的区别

    2024-07-12 19:00:02       15 阅读
  11. 补充一下MySQL的索引用法及应用场景

    2024-07-12 19:00:02       18 阅读
  12. LeetCode //C - 213. House Robber II

    2024-07-12 19:00:02       18 阅读
  13. 云WAF如何帮助政府网络进行安全防御

    2024-07-12 19:00:02       17 阅读