てくなべ (tekunabe)

ansible / network automation / 学習メモ

[Python] 2つファイルの内容をリストにして差分を表示する

はじめに

こちらの記事を拝見し、紹介されている Python のコードの別解を考えてみます。

fugulog.hatenablog.com

  • 動作確認環境

やっていること

背景はここでは割愛しますが、docker イメージ名がリスト化された2つのファイル

  • checklist.txt
  • imagelist.txt

があり、checklist.txt にあって、imagelist.txt にないイメージ名を画面に表示するものです。

たとえば、2つのファイルがそれぞれ

  • checklist.txt
image1
image2
image3
image4
image5
  • imagelist.txt
image1
image3
image5

という状態であれば、

image2
image4

と出力します。

元のコード

#checkimage.py

checklist = open("checklist.txt","r")
imagelist = open("imagelist.txt", "r")

checklists = []
imagelists = []

for line1 in checklist:
    checklists.append(line1)

for line2 in imagelist:
    imagelists.append(line2)

for check in checklists:
    if check not in imagelists:
        print(check.rstrip('\n'))

checklist.close()
imagelist.close()

わざわざ配列に入れる必要はないのかもしれませんが、そこは知識と時間不足でした、、 もしもっとスマートな書き方を知っている方は教えてください🙇‍♀️

とのことですので、私なりに4パターンほど考えたり、調べたりしました。


■ パターン1: ファイル読み込みを with で囲う

ファイルを暗黙的、かつ確実に close させるために open を with で囲うパターンです。 他は、元のコードと同じです。

checklists = []
imagelists = []

with open("checklist.txt", "r") as f:
  for line1 in f:
    checklists.append(line1)

with open("imagelist.txt", "r") as f:
  for line2 in f:
    imagelists.append(line2)

for check in checklists:
    if check not in imagelists:
        print(check.rstrip('\n'))


■ パターン2: リスト化に splitlines() を利用する

パターン1をベースにして、ファイル内容のリスト化に splitlines() を利用するパターンです。 受け取るリストの初期化と append が不要になります。また、改行区切りで split されるので、表示時の .rstrip('\n') も不要になるはず。

with open("checklist.txt", "r") as f:
    checklists = f.read().splitlines()

with open("imagelist.txt", "r") as f:
    imagelists = f.read().splitlines()

for check in checklists:
    if check not in imagelists:
        print(check)


■ パターン3: 差分を - で求める

パターン2をベースにして、差分を - で求めるパターンです。 比較する際のループが不要になります。ただし、セットに変換するので順番は維持されません。

with open("checklist.txt", "r") as f:
    checklists = f.read().splitlines()

with open("imagelist.txt", "r") as f:
    imagelists = f.read().splitlines()

diff = list(set(checklists) - set(imagelists))

for check in diff:
    print(check)


■ パターン4: 差分を filter で求める

パターン3でベースにして、順番を維持するやり方がないかと調べたところを以下のページが参考になりました。

lcjvEm - Online Python3 Interpreter & Debugging Tool - Ideone.com

(コメントに「重複も保持される」とありますが「順番も」のことだと思います)

差分を filter で求めるパターンです。

with open("checklist.txt", "r") as f:
    checklists = f.read().splitlines()

with open("imagelist.txt", "r") as f:
    imagelists = f.read().splitlines()

diff = list(filter(lambda x: x not in imagelists, checklists))  

for check in diff:
    print(check)


おわりに

表示の仕方を含めるともっといろいろな方法があると思います。