open関数でファイルをオープンして、readメソッドでファイルの内容を読み込んで、最後にcloseメソッドでファイルをクローズする。それがふつ~かもしれませんが、もっとカンタンに書く方法がありますよ?
以下は「example.txt」という名前のファイルをopen関数で開いて、その内容を変数contentに読み込むPythonコードである。しかし、このコードはより簡潔に記述できる。どんな書き方が考えられるだろうか。
file = open('example.txt', 'r')
content = file.read()
file.close()
どうもどうも。HPかわさきです。今回も正解は1つじゃないパターンです。多くの人が「ふつ~、コレでしょ!」という書き方はアレでしょう。でも、筆者はそれとは違うやり方が好みです。特にサンプルコードの中でファイルの内容を見せたいようなときには、違うやり方の方が簡単に書けますからね。さて、筆者の好みのコードとはどんな書き方でしょう。
正解のコード例を以下に示します。
# その1
with open('example.txt', 'r') as file:
content = file.read()
# その2
from pathlib import Path
content = Path('example.txt').read_text()
その1としたのはwith文を使う例です。多くの方はこちらを思い浮かべると思います。with文を使うことで、ファイルのクローズが必ず行われるようになることから問題文のようなコードではなく、こちらを使うことがPythonでは一般的ともいえますね。
その2のコードはpathlibモジュールのPath.read_textメソッドを使う例です。ファイルのオープン、内容の読み込み、ファイルのクローズを1つのメソッドで行えるのでより簡潔ともいえます。
問題文のコードは次のようなものでした。
file = open('example.txt', 'r')
content = file.read()
file.close()
このコードには次のような考慮点があります。
もちろん、上のコードではcloseメソッド呼び出しが書かれているので、readメソッドで例外が発生しなければ、ファイルは確実にクローズされるでしょう。しかし、人という生き物は物忘れをするものです。ファイルのオープンからクローズまでにたくさんのコードを書かなければならないときには、closeメソッドを書き忘れるなんてことはよくあるかもしれません。
closeメソッドの呼び出しを忘れると、そのファイルがずっとオープンされっ放しになって、無駄なリソースをプログラムの実行中に消費し続けることになります。ファイルがオープンされ続けているので、他のプログラムがそのファイルを使いたいのに使えないといった状況が発生するかもしれません。
closeメソッド呼び出しを書き忘れなくても、それまでの間に何らかの原因で例外が発生すれば(問題文ならreadメソッドで例外が発生すれば)、closeメソッドが実行されないまま、上位のフレームに制御が移り、プログラムが終了してしまうかもしれません。
書き込みモードでファイルをオープンしていたとすると、writeメソッドを呼び出してファイルへ何かを書き込んだつもりでも、closeメソッドが呼ばれるまではバッファリングにより書き込みが遅延されたままとなり、ファイルへの書き込みが行われない可能性もあるでしょう。
冷蔵庫の開け閉めと同じで、使い終わったファイルは確実かつ迅速にクローズすることが重要です。それを可能にしてくれるのがwith文です。
with文はPythonのコンテキストマネジャーと呼ばれる機構を使って、with文のブロックを実行する前に何らかのコンテキストを設定し、ブロックの実行後にそのコンテキストの後処理を確実に行えるようにします。
with open('example.txt', 'r') as file: # コンテキスト(ファイル)の用意
content = file.read() # コンテキストの利用
# ブロックが終わったところでファイルがクローズされる
この例ではコンテキストとはファイルのことだと考えてよいでしょう。上の「with open('example.txt', 'r') as file:」という部分で、with文のブロックである「content = file.read()」を実行する前にファイルをオープンして、ブロックの内部でそれを利用できるようにし、ブロックの実行後にはファイルをクローズしてくれるということです。
with文のブロックを実行中に例外が発生した場合でも、コンテキストの後処理は行われるので、ファイルのクローズ忘れを心配する必要がなくなります。例えば、以下のコードを実行すると、ファイルがクローズされたかどうかを示すfile.closed属性はTrueになります。
with open('example.txt', 'r') as file:
content = file.read()
raise Exception('Error')
print(file.closed) # True
そして、今見てきたような処理をさらに抽象化したのが、pathlibモジュールのPath.read_textメソッドです。ファイルのオープンや読み込み、クローズといった処理を1つのメソッドにまとめたものと考えてよいでしょう。
from pathlib import Path
content = Path('example.txt').read_text()
実はread_textメソッドの内部ではwith文を使って、ファイルをオープンし、その内容をreadメソッドで読み出して、ファイルをクローズし、その内容を返送するようになっています。
というわけで、筆者が好んで使っている違うやり方は「その2」のPath.read_textメソッドを使うものでした。皆さんもガンガン使ってくださいね。
また、カンタンではなく、単に書き換えるというだけであれば、try-finally文を使ってファイルのクローズが確実に行われるようにするなどの方法が考えられます。が、多くの場合はwith文を使うか、pathlibモジュールを使うかになるんじゃないかなぁ。
では、これら2つの方法をどのように使い分ければよいかというと、次のような指針が考えられるでしょう。
read_textメソッドはファイルの内容をまとめて読み込むので、そのサイズが大きいときにはメモリの消費量も増えてしまいます。サイズが大きなファイルから、ある程度のサイズごとに読み込みを行い、それに対して何らかの処理を加えるといったループ処理をしたいのであれば、with文を使うのがよいでしょう。
with文については「Python入門」の「ファイル操作と例外処理」でも取り上げているので、興味のある方はそちらもぜひご覧ください。
なお、今回の問題を考えたのはDeep Insider編集部の一色さんでした。皆さんも「こんな問題あるよ!」ってのがあったら、HPかわさきまでお知らせくださいね。
初心者向け、データ分析・AI・機械学習・Pythonの勉強方法 @ITのDeep Insiderで学ぼう
Copyright© Digital Advantage Corp. All Rights Reserved.
Deep Insider 記事ランキング