Python 所有文章传送门 |
---|
【Python】所有文章传送门 |
大多数容器对象都可以使用 for 语句。而迭代器的使用非常普遍,并使得 Python 成为一个统一的整体。在幕后,for 语句会在容器对象上调用 iter()
。 该函数返回一个定义了 __next__()
方法的迭代器对象,此方法将逐一访问容器中的元素。 当元素用尽时,__next__()
将引发 StopIteration
异常来通知终止 for 循环。
实现了 __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,
生成器函数使用 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,
与其相关的一个是生成器表达式。某些简单的生成器可以写成简洁的表达式代码,所用语法类似列表推导式,但外层为圆括号而非方括号。
比如:
>>> 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']
这是一个可用于枚举可迭代对象中元素的迭代器,它会以 (计数, 元素)
的形式返回:
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: 黄轩#毛岸英
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)中的指定元素 - 小邓在森林 。
使用指定函数处理可迭代对象的每个元素。示例代码如下:
>>> list(map(abs, [-1, 100, 0, -56]))
[1, 100, 0, 56]
它可以拼接多个可迭代对象的元素。示例代码如下:
>>> 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)]