关于Iterator的一切

2022/05/09 Python 共 2098 字,约 6 分钟

作为一个半吊子的Python选手,对于iterable,iterater,generator长期处于半知半解的状态。今天来厘清一下,它们到底是什么以及实用方面的意义:

Iterable

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语句处继续执行。

文档信息

Search

    Table of Contents