作为一个半吊子的Python选手,对于iterable,iterater,generator长期处于半知半解的状态。今天来厘清一下,它们到底是什么以及实用方面的意义:
Iterable
根据Python官网上的定义,任何有__iter_()
或__getitem__()
的类都是iterable的。在Python内置的类型中,list
, str
, tuple
, dict
, file
都是iterable的。Iterables可以用for
迭代, 并且iterable可以用iter()
函数转换为iterator
。
iterator
引用:Python/Iterator and Generator 关系
为什么需要iterator呢?主要是为了节省内存,把iterator看作流,每次只需要从内存中读取当前的元素,而不需要将所有的元素都读入内存。可以用__next_()
来实现。当然iterator也可以用for循环迭代。
比如,对于文件:
import os
base_dir = r"F:\python-office"
log_file = os.path.join(base_dir, "access.log") # 2G+ 日志文件
log_result = open(log_file) # 打开日志文件
log_data = log_result.readlines() # 一行行读进内存
log_result.close() # 关闭
这种情况下的操作是将整个文件读入内存的:
前面提到file在Python中是一个Iterable的类,在下面我经常看到读文件的代码中,file读入是Itertor的方式:
import os
from collections import Iterator,Iterable
base_dir = r"F:\python-office"
log_file = os.path.join(base_dir, "access.log")
def read_log(log_file):
with open(log_file) as f:
print("Iterator:",isinstance(f, Iterator))
for line in f:
print(line)
break
read_log(log_file)
>>>
Iterator: True
122.137.241.106 - - [07/Mar/2018:14:58:03 +0800] .......xxxxxxxxxxxx
Generator
引用:filter
创建generator有两种方式,第一种就是使用()
:
>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x1022ef630>
另一种方式就是使用yield
语句:
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
它所返回的是generator iterator,它的用法是和iterator一样的。
比较需要理解的地方就是yield的执行过程。下面用埃氏筛法计算素数的例子来举例:
首先,列出从2
开始的所有自然数,构造一个序列:
2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, …
取序列的第一个数2
,它一定是素数,然后用2
把序列的2
的倍数筛掉:
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, …
取新序列的第一个数3
,它一定是素数,然后用3
把序列的3
的倍数筛掉:
5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, …
取新序列的第一个数5
,然后用5
把序列的5
的倍数筛掉:
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, …
不断筛下去,就可以得到所有的素数。
def _odd_iter():
n = 1
while True:
n = n + 2
yield n
def _not_divisible(n):
return lambda x: x % n > 0
def primes():
yield 2
it = _odd_iter() # 初始序列
while True:
n = next(it) # 返回序列的第一个数
yield n
it = filter(_not_divisible(n), it) # 构造新序列
# 打印1000以内的素数:
for n in primes():
if n < 1000:
print(n)
else:
break
这里,最难理解的就是generator函数和普通函数的执行流程不一样。普通函数是顺序执行,遇到return
语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()
的时候执行,遇到yield
语句返回,再次执行时从上次返回的yield
语句处继续执行。
文档信息
- 本文作者:weownthenight
- 本文链接:https://weownthenight.github.io/2022/05/09/%E5%85%B3%E4%BA%8EIterator%E7%9A%84%E4%B8%80%E5%88%87/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)