[Pythonクイズ]if文 vs. try文、ファイル削除で“Python風”なのは? あなたはドッチ派?:Pythonステップアップクイズ
ファイルを削除するコードの書き方にもいろいろあります。あなたはどっちのスタイルのコードを書いていますか? ちょっと考えてみてください。
【問題】
以下はファイルを削除する典型的なコード例である。選択肢1と選択肢2のどちらがよりPython風なやり方か、自分が推すスタイルを答えよ。
from os import remove
from os.path import exists
# 選択肢1
if exists('test.txt'):
remove('test.txt')
# 選択肢2
try:
remove('test.txt')
except FileNotFoundError:
print('File Not Found')
どうもHPかわさきです。
難易度判定ばっかりやるのもナニなので、今回は3つのLLMに「自分が他言語でのプログラミング経験もあるPythonの初中級者だとしたら、選択肢1と選択肢2のどっちのスタイルでコードを書くか」を聞いてみました。筆者がどっちのスタイルで書いているかも含めて「推しのスタイル」は次のようになりました。ババーン!
筆者だったらどう書くか? というと、普段は選択肢1のスタイルでコードを書くことが多いですね。そして、LLMに聞いた結果は次のようになりました。
- 選択肢1:ChatGPT、Gemini(+筆者)
- 選択肢2:Claude
これを見ると選択肢1が優勢ですが、よりPython風なのはどっちなのでしょうか?
それはそうと、ここでLLMとの問答をやっていると、筆者が好きなことを書き散らす場所がなくなっちゃうんですよ。ちょっとやり方を考えないといけませんねぇ。
【答え】
よりPython風とされているのは「選択肢2」の書き方です。
選択肢1のやり方は「LBYL」というスタイルで、選択肢2のやり方は「EAFP」というスタイルです。そして、Pythonの用語集にある「EAFP」の説明では「Pythonで広く使われているコーディングスタイル」と書いてあることから、ここでは選択肢2を「よりPython風」であるとしています。
ChatGPT、Gemini、そして筆者が選択肢1を選んだにもかかわらず、選択肢2の方が「よりPython風」となりました。「これは! 絶対に何かヤラかしてやがる!」という陰謀論者の皆さん、その通りです。LLMに素直に質問すると、みんながみんな「選択肢2です」って言ってくるものだから、選択肢1が選ばれるようにプロンプトで誘導してみました。え? 筆者? 普段は選択肢1のスタイルでコードを書くことは確かです(ダメじゃん)。
【解説】
Pythonのドキュメントによると選択肢1のやり方である「LBYL」は「Look Before You Leap」(転ばぬ先の杖)の略で、これは「前提条件が満たされているかどうかをチェックしてから、何らかの処理を実行するスタイル」のことです。
from os import remove
from os.path import exists
# 選択肢1
if exists('test.txt'):
remove('test.txt')
if文で前提条件が満たされているかどうかをチェックしてから、該当するブロックで何らかの処理を行うようなコードの書き方になります。Cやそこから派生する言語に慣れている方には見慣れたスタイルと言えますね。筆者もよくこういうコードを書いています。
でも、このやり方が問題を招くこともあります。例えば、上のコードでexists関数によるファイルの存在確認が取れたとします。そして、remove関数を呼び出す直前に他のプログラムがこのファイルを削除してしまったらどうでしょう? ファイルの存在確認が通ったのにもかかわらず、remove関数はFileNotFoundError例外を発生させることになります。何のための存在確認だったのか、分かりませんよね(こうしたことが発生する確率は低いかもしれませんが、可能性はゼロではありません)。
一方、選択肢2のやり方である「EAFP」とは「Easier to Ask for Forgiveness than Permission」(許可を得るよりも許してもらう方がカンタン)の略です。「取りあえずやって、問題があればそれに対処するというスタイル」といえるでしょう。「案ずるよりは産むがやすし」のようにも思えますが、「案ずるよりも産んでみて、俺たちの戦いはそこからだ」の方が近いかもしれませんね。
from os import remove
# 選択肢2
try:
remove('test.txt')
except FileNotFoundError:
print('File Not Found')
こちらは意図した処理をtry文で囲んで、問題があれば(例外が発生したら)対応するexcept節で処理をするスタイルのコードの書き方になります。
このスタイルでは「exists関数でファイルが存在することを確認したのに、直後に削除されちゃったので、remove関数で失敗した」のような事態が発生することはありません。ちなみにLBYLスタイルのコードで例外に対処しようと思ったら、どんなコードになるでしょう。たぶん、こんなコードを書くのではないでしょうか。
try:
if exists('test.txt'):
remove('test.txt')
except FileNotFoundError:
print('File Not Found')
このコードならexists関数でファイルが存在することを確認した直後にそれが削除されても、全体がtry文で囲まれていて、FileNotFoundError例外を捕捉するexcept節があるので、それに対処できます。
でも、try文でremove関数を囲んでいるんだから、そもそもexists関数による存在チェック自体が必要ないですよね。ということは、「LBYLのコードをtry文で囲む」というこのコードは次のように書けば十分です。
try:
remove('test.txt')
except FileNotFoundError:
print('File Not Found')
というわけで、結局はEAFPなスタイルのコードに落ち着いてしまうのでした。例外を無視できる環境(学習や実験的な環境)では、LBYLなコードでも十分でしょう。しかし、そうではない状況ではおのずとこうしたコードを書くようになっていくのかもしれないですね。
今回はコーディングスタイルについてのお話でした。Python入門では「Pythonコーディングスタイルガイド」でコーディングスタイルについて話をしていますが、これはPEP8に合わせた命名の仕方などについての話なので、今回のLBYLとかEAFPは出てきていません。興味があれば目を通してみてください。
Copyright© Digital Advantage Corp. All Rights Reserved.