
本文介绍如何使用 csv.dictreader 流式读取制表符分隔的 csv 文件,避免内存溢出,同时正确提取出生年份并计算当前(2024年)年龄的中位数,解决 ‘_csv.reader’ Object is not subscriptable 类型错误。
在处理大型 CSV 文件时,为节省内存,应避免将整个文件一次性读入列表或 DataFrame。python 的 csv 模块提供了流式读取能力,但需注意 csv.reader 与 csv.DictReader 的关键区别:前者返回每行为 list(需用索引访问字段,如 row[2]),后者返回每行为 dict(支持按列名访问,如 row[‘birth_year’])。原代码中误将 csv.reader 对象当作字典使用(data[‘birth_year’]),导致 TypeError。
正确做法是改用 csv.DictReader,它自动将首行解析为字段名,并为后续每一行构建键值映射。此外,我们采用生成器表达式 (2024 – int(row[‘birth_year’]) for row in reader) 实现惰性求值——年龄值在中位数计算过程中逐个生成,全程不构建完整列表,真正实现低内存占用。
以下是完整、健壮的实现(含基础异常处理):
import csv from statistics import median def median_age(filename): try: with open(filename, 'r', encoding='utf-8') as file: reader = csv.DictReader(file, delimiter='t') # 验证必需字段是否存在 if 'birth_year' not in reader.fieldnames: raise ValueError(f"Missing required column 'birth_year' in {filename}") # 生成年龄序列(跳过空值或无效年份) ages = [] for row in reader: try: birth_year = int(row['birth_year'].strip()) if 1900 <= birth_year <= 2024: # 合理年份范围过滤 ages.append(2024 - birth_year) except (ValueError, TypeError): continue # 跳过无法解析的行 if not ages: raise ValueError(f"No valid birth_year found in {filename}") return median(ages) except FileNotFoundError: raise FileNotFoundError(f"File '{filename}' not found.") except Exception as e: raise RuntimeError(f"Error processing {filename}: {e}")
使用示例:
假设 data.csv 内容如下(制表符分隔):
first last birth_year paul henry 2019 bill thompson 1995 mary allen 2003 jennifer davis 2015 liz morgan 1999
调用 median_age('data.csv') 将返回 21.0(对应年龄序列 [5, 29, 21, 9, 25] 的中位数)。
关键注意事项:
- ✅ 始终指定 encoding='utf-8' 防止中文或特殊字符报错;
- ✅ 使用 DictReader 而非 reader 实现列名访问;
- ✅ 通过生成器+显式循环+过滤,兼顾内存效率与数据鲁棒性;
- ❌ 避免在 csv.reader 上直接使用方括号索引(如 row['xxx']);
- ⚠️ 若文件无标题行,需手动传入 fieldnames= 参数;若分隔符非制表符,请同步调整 delimiter。
该方案适用于 GB 级日志或用户档案 CSV,单次遍历、零中间列表、错误可追溯,是生产环境推荐的轻量级年龄统计实践。