まとめ
csv.DictReaderオブジェクトを複数回参照してはいけない。
使いまわしたい場合、都度オブジェクトを再生成して参照するべき。
経緯
下記コードの実行時、1度目と2度目で出力結果が変わった。
原因はcsv.DictReaderがイテレータであることにより、1度目のループでイテレータが消費された為であった。
def is_in_targets(data, targets: csv.DictReader):
if "No." not in targets.fieldnames:
return False
# csv.DictReaderをforループにかける
for row in targets:
if data["番号"] not in row["No."]:
continue
if data["年齢"] in row["Age"]:
return True
return False
data = {"番号": "1", "年齢": "12"}
targets = """No.,Age
1,12"""
targets_csv = csv.DictReader(StringIO(targets))
print(is_in_targets(data, targets_csv)) # 出力結果: True
print(is_in_targets(data, targets_csv)) # 出力結果: False
対策
1. リスト変換
- for row in targets:
+ for row in list(targets):
結果:失敗
list()を実行した場合もイテレータが消費されてしまう
2. オブジェクト再生成
DictReaderオブジェクトを直に渡すのではなく、元のデータを渡した後に関数内でDictReaderオブジェクトを生成する
- def is_in_targets(data, targets: csv.DictReader):
+ def is_in_targets(data, targets_str: str):
+ targets = csv.DictReader(StringIO(targets_str))
結果:成功
data = {"番号": "1", "年齢": "12"}
targets = """No.,Age
1,12"""
print(is_in_targets(data, targets)) # 出力結果:True
print(is_in_targets(data, targets)) # 出力結果:True