今日の目標
ファイルを使って、プログラムの実行結果を記録に残します。またその記録を再利用します。
- ファイル の操作
- open/close/flush : file mode、 ファイルオブジェクト
- read/write/readline/readlines/writelines
with
文import
文
前回2つの質問をお尋ねしました。 まずはこの質問について解説しておきます。
ここでクイズです。
a=100 b=100とした時、
print( a == b, a is b, id(a) == id(b))はどんな結果になるでしょう? 結果を予想してから実行してみましょう。
実際に実行して見ましょう。
a=100
b=100+0
print(f"\t {a=:}, {b=:},\t{(a == b)=:}, {(a is b)=:}, {(id(a) == id(b))=:}")
a=100, b=100, (a == b)=True, (a is b)=True, (id(a) == id(b))=True
他の値でも試してみましょう。
for i in (100,256,257,1024):
a=i
b=i+0
print(f"{i= :4d}, {a= :4d},{b= :4d}, {(a == b)= :}, {(a is b)= :}, {(id(a) == id(b))= :}")
i= 100, a= 100,b= 100, (a == b)= True, (a is b)= True, (id(a) == id(b))= True i= 256, a= 256,b= 256, (a == b)= True, (a is b)= True, (id(a) == id(b))= True i= 257, a= 257,b= 257, (a == b)= True, (a is b)= False, (id(a) == id(b))= False i= 1024, a= 1024,b= 1024, (a == b)= True, (a is b)= False, (id(a) == id(b))= False
Pythonでは256以下の正の整数は特別扱いされています。 これらの数値については、インタプリタの初期化時にその数値のオブジェクトが生成/登録されています。 (短い文字列についても同様の特別の処理が行われています。)
「変数へ値を代入する」、「変数の値を更新する」といった操作の意味はプログラム言語によって 若干の違いがあります。ここではPythonとC/C++言語を例に、これらの操作を図解してみました。
C/C++ の代入 | Pythonの代入 |
---|---|
C/C++言語などの伝統的なプログラム言語では、変数名はデータを保持する実体(メモリ)につけられています。この関係は、プログラムの実行中に変わることはありません。
それに対して、Pythonなどのインタプリタ型言語などでは、変数名はデータの実体を参照する際に使われます。 変数名と参照される実体との関係はプログラムの実行に従って変わっていきます。
((r:=1) or (r:=r+2)) and (r:=r+4)
および
(r:=1) and (r:=r+2)) or (r:=r+4)
の結果はそれぞれ何になりますか?
試してみましょう。
print(f"{((r:=1) or (r:=r+2)) and (r:=r+4) = :}")
print(f"{((r:=1) and (r:=r+2)) or (r:=r+4) = :}")
((r:=1) or (r:=r+2)) and (r:=r+4) = 5 ((r:=1) and (r:=r+2)) or (r:=r+4) = 3
((r:=1) or (r:=r+2)) and (r:=r+4)
Pythonは式を左から見て行って、評価の必要な項から順次評価を進めます。
最初の代入式でr
は 1 に設定され、代入式の値自体も 1 になります。
(1 or (r:=r+2)) and (r:=r+4)
ここで、(1 or (r:=r+2))
の論理式としてはTrue
であることが確定しますから、(r:=r+2)
を計算する必要はありません。
従って、r
は1のままです。
1 and (r:=1+4)
この式の値は、r:=1+4
の値が必要です。この式の値は 1+4
つまり5
で、同時にr
の値も5となります。
1 and 5
5
ということで、((r:=1) or (r:=r+2)) and (r:=r+4)
の値は 5 となります。
((r:=1) and (r:=r+2)) or (r:=r+4)
( 1 and (r:=1+2)) or (r:=r+4)
( 1 and 3) or (r:=3+4)
3 or (r:=3+4) # 第2項(r:=3+4)は評価の必要なし,rは3のまま
3
と評価がすすみ、((r:=1) and (r:=r+2)) or (r:=r+4)
の値は3となりました。
or
演算子の右側の項は、評価されない(実行されない)可能性があるということです。
練習として、
print(f"{((r:=0) or (r:=r+2)) and (r:=r+4) = :}")
print(f"{((r:=0) and (r:=r+2)) or (r:=r+4) = :}")
を考えてみましょう。
答えは、....
print(f"{((r:=0) or (r:=r+2)) and (r:=r+4) = :}")
print(f"{((r:=0) and (r:=r+2)) or (r:=r+4) = :}")
((r:=0) or (r:=r+2)) and (r:=r+4) = 6 ((r:=0) and (r:=r+2)) or (r:=r+4) = 4
となりました。
Pythonでのファイルの取り扱いの基礎を学びます。
計算結果のファイルへの保存:
open
/ .close
/ .write
/ .writelines
with
文保存したデータを再び読み込む:
.reaad
/.readline
/.readlines
シェルコマンド cat
を使えば、ファイルの中身を端末に印刷することができます。
!cat sample.txt
あのイーハトーヴォの すきとおった風、 夏でも底に冷たさをもつ青いそら、 うつくしい森で飾られたモリーオ市、 郊外のぎらぎらひかる草の波。
このcat
と同じように、ファイルの中身を端末に印刷するプログラムを作ってみます。
def ファイル内容の表示(fn:str="sample.txt"):
print("ファイル:{0:s}の内容\n".format(fn))
with open(fn,mode="rt") as fin:
for line in fin:
print(line,end="")
早速実行してみましょう。
ファイル内容の表示("sample.txt")
ファイル:sample.txtの内容 あのイーハトーヴォの すきとおった風、 夏でも底に冷たさをもつ青いそら、 うつくしい森で飾られたモリーオ市、 郊外のぎらぎらひかる草の波。
cat
コマンドと同じ様に、ファイルの中身が印刷されました。
プログラムの中身を順番にみて行きましょう。
open(fn,mode="rt")
¶プログラム中でファイルを使うに、まずファイルを開きます。
pythonプログラム中で、ファイルを開くにはopen()
関数を使います。
open()
関数には、開くファイルのファイル名と、ファイルの使い方を示すmode
を与えます。
fin=open("sample.txt",mode="rt")
は、"sample.txt"という名前のファイルを, 読み出し用(r
)のテキストファイル(t
)として開きます。
開いたファイルはopen
が返すオブジェクト fin
を通じて操作(.read/.write
などがある)をします。
文字 | 意味 |
---|---|
'r' | 読み込み用に開く (デフォルト) |
'w' | 書き込み用に開き、まずファイルを切り詰める |
'a' | 書き込み用に開き、ファイルが存在する場合は末尾に追記する |
'x' | 排他的に生成して開く、ファイルが存在する場合は失敗する |
'b' | バイナリモード |
't' | テキストモード (デフォルト) |
'+' | ファイル内容の更新 (読み書き) |
w+
とr+
の違いは オープンした時に内容が切り詰められる(w+
)か内容に追加(r+
)かの違いがある。
ファイル オープン時の モード指定 (r/a/w/x
)によるopen直後の読み書き位置とファイル内容の違いを図で示します。
.close()
¶ファイルを使い終わったら、ファイルを閉じる.close()
ことが 必要 です。
さもないと、せっかく書き込んだデータが失われるかもしれません。
with
文を使って,自動的に、ファイルを閉じる。: with open(fn,mode="rt") as fin:
¶ファイルを使い終わった時に、ファイルが確実に閉じられるように次の構文(with
文)を使います。
with open(fn,mode="r") as fin:
... # finを通じてファイルの操作(read/writeなど)を行う。
...
... # ここに到達すると ファイル fin は閉じられている。
ファイルの中身を「一行毎に読み込んで、処理する」
には、for
文を次の形で使います。
for line in fin:
print(line,end="")
行の終わりは、\n
, \r\n
\r
のいずれでも対応します(Universal newlines).
読み込んだ行末のEOL(End of Line)は"\n"となっています。 (newline 引数を指定することで、変更可能です。)
歴史的な事情もあり、推奨の方法以外にも行毎の処理を行う方法がpythonには存在します。
ここで、それらの方法も紹介しますが、 明確な理由(例えば、古いバージョンのPythonしか使えないとか)がない限り、 推奨の方法を使うのが良いでしょう。
def ファイル内容の表示F(fn="sample.txt"):
print("\nファイル:{}の内容(assign expression)\n".format(fn))
with open(fn,mode="r")as fin:
while (line:=fin.readline()): # ファイルから一行分だけを読み込む。
print(line,end="")
ファイル内容の表示F()
ファイル:sample.txtの内容(assign expression) あのイーハトーヴォの すきとおった風、 夏でも底に冷たさをもつ青いそら、 うつくしい森で飾られたモリーオ市、 郊外のぎらぎらひかる草の波。
.readlines()
や.read()
メソッドを使うと、ファイル中の全てのデータを一気に読み出すことができます。
ファイルのサイズが非常に大きい時には、大量のメモリが必要となります。また、全てのデータを 読み込むまで、プログラムはそこで待たされることになります。
ということで、.readlines()
や.read()
は特別な事情がない限り、使わない方が良いでしょう。
def ファイル内容の表示L(fn="sample.txt"):
print("\nファイル:{}の内容(readlines)\n".format(fn))
with open(fn,mode="r")as fin:
for line in fin.readlines(): # ファイルの中身を行毎のリストとして読み込む。
print(line,end="")
def ファイル内容の表示B(fn="sample.txt"):
print("\nファイル:{}の内容(read)\n".format(fn))
print(open(fn,mode="r").read(),end="") #ファイルの内容を一気に読み込む。
ファイル内容の表示L()
ファイル内容の表示B()
ファイル:sample.txtの内容(readlines) あのイーハトーヴォの すきとおった風、 夏でも底に冷たさをもつ青いそら、 うつくしい森で飾られたモリーオ市、 郊外のぎらぎらひかる草の波。 ファイル:sample.txtの内容(read) あのイーハトーヴォの すきとおった風、 夏でも底に冷たさをもつ青いそら、 うつくしい森で飾られたモリーオ市、 郊外のぎらぎらひかる草の波。
ファイルの本体は全てバイトデータの並びです。その意味では、全てのファイルはバイナリファイルとして読み込めます。
テキスト、画像、音声、動画、その他さまざまなデータをそれらのデータ形式に合った形で読み込むことが必要です。
open()
関数では、ファイルの内容がテキストであることがわかっていれば(t
)
エンコーディング(encoding
)を指定することで、テキスト(ユニコード)として読むために必要な処理をプログラム処理系が行なってくれます。
未知のデータファイルあるいは独自に定義されたでーた形式を取り扱うためには、バイナリファイルとして取り扱う必要がありますが、 一般ユーザーがそのような場面に遭遇する機会は無くなってきています。
with open("sample.txt",mode="r",encoding="utf-8") as fin:
print("テキスト:", fin.readline(),end="")
with open("sample.txt",mode="rb") as fin:
line=fin.readline()
print("バイナリ:", line)
print("デコード:", line.decode("utf-8"),end="")
テキスト: あのイーハトーヴォの バイナリ: b'\xe3\x81\x82\xe3\x81\xae\xe3\x82\xa4\xe3\x83\xbc\xe3\x83\x8f\xe3\x83\x88\xe3\x83\xbc\xe3\x83\xb4\xe3\x82\xa9\xe3\x81\xae\n' デコード: あのイーハトーヴォの
次に、以前に作成した数表印刷プログラムを元に、数表をファイルに書き出すプログラムを作ってみます。
def 数表の印刷():
print ("フィボナッチ数の数表".center(66,"_"))
print ( " {0}".format(
",".join((" {0:02d}".format(c) for c in range(10))))
)
for r in range(0,30,10):
print("{0:02d}:{1}".format(
r,
",".join(("{0:6d}".format(fib(c)) for c in range(r,r+10)))
))
端末に文字列を書き出す関数print()
をファイルへの書き出しの関数(メソッド).write()
に書き換えればよさそうです。
from fibonacci import fib
def 数表の印刷():
print ("フィボナッチ数の数表".center(66,"_"))
print ( " {0}".format(
",".join((" {0:02d}".format(c) for c in range(10))))
)
for r in range(0,30,10):
print("{0:02d}:{1}".format(
r,
",".join(("{0:6d}".format(fib(c)) for c in range(r,r+10)))
))
数表の印刷()
____________________________フィボナッチ数の数表____________________________ 00, 01, 02, 03, 04, 05, 06, 07, 08, 09 00: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 10: 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765 20: 10946, 17711, 28657, 46368, 75025,121393,196418,317811,514229,832040
from fibonacci import fib
def 数表の保存0(fn="Fibonacci_table.txt"): # 第0試作
with open(fn,mode="wt") as fout: # 書き出し(w)用にファイルを開く
fout.write("フィボナッチ数の数表".center(66,"_")) # print() -> fout.write()
fout.write ( " {0}".format(
",".join((" {0:02d}".format(c) for c in range(10))))
)
for r in range(0,30,10):
fout.write("{0:02d}:{1}".format(
r,
",".join(("{0:6d}".format(fib(c)) for c in range(r,r+10)))
))
試してみましょう。
if __name__ == "__main__":
数表の保存0()
ファイル内容の表示("Fibonacci_table.txt")
ファイル:Fibonacci_table.txtの内容 ____________________________フィボナッチ数の数表____________________________ 00, 01, 02, 03, 04, 05, 06, 07, 08, 0900: 1, 1, 2, 3, 5, 8, 13, 21, 34, 5510: 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 676520: 10946, 17711, 28657, 46368, 75025,121393,196418,317811,514229,832040
数値は表示されましたが、改行されずに一行に出力されてしまいました。
print()
とは異なり、ファイルの.write()
は改行を追加してくれません。
明示的に改行コード "\n" をファイルに書き込む必要があります。
各行を書き出す際に "\n" を追加するように書き換えてみます。
def 数表の保存1(fn="Fibonacci_table.txt"):
with open(fn,mode="w") as fout:
fout.write("{0}\n".format( # \n 追加
"フィボナッチ数の数表".center(66,"_"))
)
fout.write ( " {0}\n".format( # \n 追加
",".join((" {0:02d}".format(c) for c in range(10))))
)
for r in range(0,30,10):
fout.write("{0:02d}:{1}\n".format( # \n 追加
r,
",".join(("{0:6d}".format(fib(c)) for c in range(r,r+10)))
))
試してみましょう
数表の保存1() #改良版でファイルに数表書き出し
ファイル内容の表示("Fibonacci_table.txt") #ファイルを読み出す
ファイル:Fibonacci_table.txtの内容 ____________________________フィボナッチ数の数表____________________________ 00, 01, 02, 03, 04, 05, 06, 07, 08, 09 00: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 10: 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765 20: 10946, 17711, 28657, 46368, 75025,121393,196418,317811,514229,832040
import
文¶数表保存のプログラムで使ったfib()
関数は別ファイル"fibonacci.py"で定義されています。(次ページを参照)
この様に、作成済みのプログラムをimport
文を使って、再利用することができます。 import
されるブログラムを**モジュール(module)と呼びます。モジュールの利用法の代表的な方法には次の様なものがあります。
インポート法 | 利用法 | 意味 |
---|---|---|
from fibonacci import fib |
print (fib(10)) |
モジュール fibonacci から fib をインポート |
from fibonacci import * |
print (fib(10)) |
モジュール fibonacci から 全てをインポート |
import fibonacci |
print( fibonacci.fib(10) |
モジュールfibonacci をインポート |
fibonacchi.py
の中身は次のようになっています。
#!python3
def fib(n:int)->int:
"""
returns the n-th fibonacci number.
"""
if n in (0, 1):
return 1
a=b=1
while (n := n-1 ) > 0:
a, b = a+b, a # a: f_{k}, b:f_{k-1}
return a
if __name__ == "__main__":
for i in range(10):
print(f"{i=:}, {fib(i)=:})
if __name__ == "__main__":
以下の部分はimport
の際には実行されません。
このモジュールの単独動作確認の際に役立ちます。
from fibonacci import fib as FIB
print( FIB(10) )
89
import fibonacci
print( fibonacci.fib(10) )
のように、モジュールをimport
した後に、モジュール名と関数名を組み合わせたfibonacci.fib()
を関数名として使います。
モジュール名は ファイルシステムと同じく階層的な構造を持っていることがあります (例:.matplotlib.pyplot
)。
import matplotlib.pyplot as pyplot
pyplot.plot([fib(i) for i in range(0,10)])
この例の様に、深い階層を持つモジュールもimport xxx.yyy.zzz as nnn
の構文を使うことで、使いやすくなります。
import fibonacci
import matplotlib.pyplot as pyplot
pyplot.plot([fibonacci.fib(i) for i in range(0,10)]);
open
/.close
/.readline
/.write
with ... as ... :
import <module>
/from <mdule> import <name>