<jupyter_start><jupyter_text>標準ライブラリ（良く使うもの抜粋）---- ref: https://docs.python.org/ja/3/library/- blog: https://slash-z.com/- github: https://github.com/KazutoMakino/PythonCourses--- Python の標準ライブラリの一覧は，公式ドキュメント (https://docs.python.org/ja/3/library/) の通りで，200 を超えます．  もちろん，全て使うかというとそうではなく，やりたい内容によっては使うものだったり，全く使わないものも出てきます．  さらに，良く使う標準ライブラリにおいても，使ったことが無いようなクラスやメソッドもあり，たまに使うものや使ったことが無いものは，大体調べながらプログラミングするのが常人のレベルと考えます．  従って，今回は，今後プログラミングしていくにあたって「あんな使い方やライブラリがあったな」というヒントや調べるきっかけを提供することを目的として，個人的によく使う標準ライブラリの内，よく使う使い方について紹介します．  といっても，よく使うライブラリも数多くあるので，その中から，以下の，特に良く使っているライブラリについて記載することとします．  以下で紹介しないライブラリについては，必要に応じて参照ください． 特に良く使っている標準ライブラリ（この記事にて紹介）:- pathlib (https://docs.python.org/ja/3/library/pathlib.html) ・・・・ファイル／ディレクトリパスを扱う- shutil (https://docs.python.org/ja/3/library/shutil.html) ・・・・ファイルの移動やコピー，圧縮／展開などの操作- time (https://docs.python.org/ja/3/library/time.html) ・・・・時間計測など- datetime (https://docs.python.org/ja/3/library/datetime.html) ・・・・日付／時差取得など 良く使っている標準ライブラリ:- json (https://docs.python.org/ja/3/library/json.html) ・・・・json ファイル／オブジェクトの生成などの操作- gc (https://docs.python.org/ja/3/library/gc.html) ・・・・ガベージコレクタ（プログラム実行中に動的に確保し不要になったメモリ領域を解放）- random (https://docs.python.org/ja/3/library/random.html) ・・・・乱数を扱う- math (https://docs.python.org/ja/3/library/math.html) ・・・・数学関数を扱う- statistics (https://docs.python.org/ja/3/library/statistics.html) ・・・・統計処理を行う- logging (https://docs.python.org/ja/3/library/logging.html) ・・・・ログ生成に用いる- typing (https://docs.python.org/ja/3/library/typing.html) ・・・・型ヒントに用いたり，型を明示的にすることにより，エディタの補完や予測変換機能が強化される- argparse (https://docs.python.org/ja/3/library/argparse.html) ・・・・コマンドライン引数やパーサーの生成／強化に用いる（スクリプトの多機能化）- pprint (https://docs.python.org/ja/3/library/pprint.html) ・・・・print よりもより綺麗に出力させる（例えば辞書型をキーと値のセットを出力する際に見やすくするなど）- sys (https://docs.python.org/ja/3/library/sys.html) ・・・・主にシステムを操作- pickle (https://docs.python.org/ja/3/library/pickle.html) ・・・・オブジェクトの直列化（読み書きが高速で，圧縮も可能なバイナリファイル .pickle の操作など） たまに使う標準ライブラリ:- glob (https://docs.python.org/ja/3/library/glob.html) ・・・・ワイルドカードなどを用いたパスのパターンマッチング（pathlib.Path.glob で代用のため，あまり用いない）- os (https://docs.python.org/ja/3/library/os.html) ・・・・OS の機能のサポート（パスや環境変数を取得するなど．パスについては pathlib を良く用いるため，個人的な os の使用頻度は低い．）- re (https://docs.python.org/ja/3/library/re.html) ・・・・正規表現／パターンマッチング- traceback (https://docs.python.org/ja/3/library/traceback.html) ・・・・スタックトレース（プログラムエラーのメッセージ）の表示- subprocess (https://docs.python.org/ja/3/library/subprocess.html) ・・・・python スクリプトからコマンドラインを実行- tempfile (https://docs.python.org/ja/3/library/tempfile.html) ・・・・一時的なファイル／ディレクトリを生成し／削除- itertools (https://docs.python.org/ja/3/library/itertools.html) ・・・・色々なイテレータ作成- functools (https://docs.python.org/ja/3/library/functools.html) ・・・・関数や引数を柔軟に呼び出すことができる- gzip (https://docs.python.org/ja/3/library/gzip.html) ・・・・`.gz` ファイル操作- zipfile (https://docs.python.org/ja/3/library/zipfile.html) ・・・・`.zip` ファイル操作- tarfile (https://docs.python.org/ja/3/library/tarfile.html) ・・・・`.tar`, `.tar.gz`, `.tgz` ファイル操作- csv (https://docs.python.org/ja/3/library/csv.html) ・・・・`.csv` ファイル操作- copy (https://docs.python.org/ja/3/library/copy.html) ・・・・浅いコピー（オブジェクトの参照）と深いコピー（同じ値を持つ全く別オブジェクト）の生成あまり使わない標準ライブラリ・・・・それ以外は省略  ライブラリやモジュールの呼び出し方法 例えば C 言語では，標準入出力関数のヘッダファイル `stdio.h` を `include ` にてインクルードした場合には，`stdio.h` にて定義されている `printf` やら `scanf` などその他もろもろの型／定義／関数／マクロなどがいきなり使えるようになりますが，例えば `printf` など使いたい定義について `stdio.h` に内包されていることを知っている必要があります．  しかし，python ではその心配はありません．  `import` によって宣言されたモジュールの関数やクラスを参照するためには，このモジュールの名前が出てくるため，「ある特定の関数がどの宣言と紐づいているか分からなくなる」ということがなくなります．  個人的に，至極納得のいく言語仕様です．  （余談ですが，C 言語にて `include ` とすると，`stdio.h` 内のマクロなどが呼び出されますが，その内の一つの `stdout` の中身に `printf` があるため，`printf` が使えるようになります (参考: http://www.c-lang.org/detail/stdio_h.html )．これが，初学者に苦し紛れで教えられるような「おまじない」の本質です．）  ライブラリやモジュールは，次のような構文で呼び出します． ```import モジュール as 代替する使用したい名前 または，import モジュール.モジュールの関数やクラス as 代替する使用したい名前 または，from モジュール import モジュールの関数やクラス as 代替する使用したい名前``` 上記の `as` 以降はあってもなくても良く，モジュール名などを省略名称で用いたい時に使います．  import されたモジュールは以下のように用います． ```モジュール（または as で定義した名前）.そのクラスや関数``` このように，モジュール名とその中身をドット "." でつなげるだけです．  とはいっても，日本語で書くと良く分からないと思うので，以下，紹介とともに例を挙げていきます．  pathlib pathlib (https://docs.python.org/ja/3/library/pathlib.html) は，ファイル／ディレクトリパスを扱う標準ライブラリです．  一般的なファイル操作全般に対して用いています．  pathlib は Path クラスとそのメソッドを用いることがほとんどです．  以下に import と基本的な使い方について紹介します．<jupyter_code>from pathlib import Path<jupyter_output><empty_output><jupyter_text>Path(パス文字列) というように，Path クラスにパス文字列を渡して各種メソッドを適用していきます．  例えば，今のディレクトリパス "./" を指定すると，<jupyter_code>pwd = Path("./")
pwd
type(pwd)<jupyter_output><empty_output><jupyter_text>というように，相対パスが得られます．  実行環境が windows なので，pathlib.WindowsPath 型となっています．  絶対パスは，resolve メソッドで得ることができます．<jupyter_code>pwd.resolve()<jupyter_output><empty_output><jupyter_text>新しくファイルを作成するときは，touch メソッドを用います．<jupyter_code>new_item = Path("./new_item.txt")
new_item.touch()<jupyter_output><empty_output><jupyter_text>指定したパスが存在するかどうかは，exists メソッドで確認できます．<jupyter_code>new_item.exists()<jupyter_output><empty_output><jupyter_text>新しくディレクトリを作成するときは，mkdir メソッドを用います．<jupyter_code>new_dir = Path("./new_dir")
new_dir.mkdir()
new_dir.exists()<jupyter_output><empty_output><jupyter_text>以下のようにもう一度作成しようとすると，FileExistsError が出ます．<jupyter_code>new_dir.mkdir()<jupyter_output><empty_output><jupyter_text>上書きしても良い場合は，引数 exist_ok に True を渡します．<jupyter_code>new_dir.mkdir(exist_ok=True)<jupyter_output><empty_output><jupyter_text>実は touch メソッドも引数 exist_ok があるのですが，デフォルトが True となっているため，省略してもエラーがでません（逆に，exist_ok=False を指定すると，FileExistsError が出ます）．  この場合，空のファイルで上書きされるのではなく，単に更新日時が上書きされるだけです．mkdir において，デフォルトが False である引数 parents に True を渡すことで，複数階層を同時に作成することができます．<jupyter_code>new_dir123 = Path("./new_dir1/new_dir2/new_dir3")
new_dir123.mkdir()
new_dir123.mkdir(parents=True)
new_dir123.exists()<jupyter_output><empty_output><jupyter_text>指定したパスがファイルかどうかの判定には is_file メソッド，ディレクトリかどうかの判定には is_dir メソッドにて調べることができます．<jupyter_code>new_item.is_file(), new_item.is_dir(), new_dir.is_file(), new_dir.is_dir()<jupyter_output><empty_output><jupyter_text>文字列型の結合は + によって可能ですが，パスの結合は階層を表す / によって結合ができます．<jupyter_code>Path("./new_dir1") / Path("new_dir2")<jupyter_output><empty_output><jupyter_text>上記でもできますが，先頭の型が pathlib.Path 型であれば，次のように，先頭以外は文字列で結合が可能です．<jupyter_code>Path("./new_dir1") / "new_dir2"<jupyter_output><empty_output><jupyter_text>階層を区切りたくないけど変数を使ってパス合体させたいという場合は，str() を用いて一旦文字列型にキャストするか，もしくは，f-string を用います．<jupyter_code>file_name = "new_item2"
file_path = Path("./new_dir1") / f"new_dir2/{file_name}.txt"
file_path, file_path.exists()<jupyter_output><empty_output><jupyter_text>touch はそのファイルを置くディレクトリが存在していれば，複数階層をまたいでも使うことができます．<jupyter_code>file_path.touch()
file_path.exists()<jupyter_output><empty_output><jupyter_text>指定した階層において，ファイル／ディレクトリの一覧を取得したい場合は，iterdir メソッドにてジェネレータを取得でき，list などで展開できます．<jupyter_code>list(pwd.iterdir())<jupyter_output><empty_output><jupyter_text>また，パスにワイルドカード `*` （アスタリスク）が指定できる glob メソッドでも調べることができます．<jupyter_code>list(pwd.glob("*"))<jupyter_output><empty_output><jupyter_text>glob メソッドの中の文字列の工夫次第で，検索するオブジェクトを指定することができます．  例えば，.txt ファイルだけにしたい場合は，<jupyter_code>list(pwd.glob("*.txt"))<jupyter_output><empty_output><jupyter_text>のようにすれば，この階層における探索は可能です．  しかし，先ほど生成した `new_dir1/new_dir2/new_item2.txt` のパスは上記では得られません．  このパスを得るためには，再帰的に調べさせる必要があり，ワイルドカードを二つと階層を表す / 用いて，例えば以下のように書くことができます．<jupyter_code>list(pwd.glob("**/*.txt"))<jupyter_output><empty_output><jupyter_text>ファイルの削除には unlink メソッド，ディレクトリの削除には rmdir メソッドを用います．  ただし，上記で指定したパスが存在しない場合は，FileExistsError が返されます．<jupyter_code>new_item
new_item.unlink()
new_item.exists()
new_dir
new_dir.rmdir()
new_dir.exists()
new_dir123<jupyter_output><empty_output><jupyter_text>注意として，rmdir は指定したパスのディレクトリが空でないと，OSError が返されます．<jupyter_code>Path("new_dir1").rmdir()
new_dir123.rmdir()
new_dir123.exists()<jupyter_output><empty_output><jupyter_text>今まで，ファイルの読み書きは open 関数を用いていましたが，pathlib.Path 型に対しては open メソッドを用いることができます．<jupyter_code>test_path = Path("./test.txt")
text = "test"

# write
with test_path.open(mode="w") as f:
    f.write(text)

# read
with test_path.open(mode="r") as f:
    print(f.read())<jupyter_output>test<jupyter_text>一応，pathlib を用いない場合は以下のように書きます．<jupyter_code>test_path = "./test.txt"
text = "test"

# write
with open(test_path, mode="w") as f:
    f.write(text)

# read
with open(test_path, mode="r") as f:
    print(f.read())<jupyter_output>test<jupyter_text>windows の環境では pathlib.Path 型のパスを文字列型にすると，人によっては見慣れない形式で出力されます．<jupyter_code>str(new_dir123)<jupyter_output><empty_output><jupyter_text>これは，windows のパスの区切りが円マーク（バックスラッシュ）であることに起因し，バックスラッシュはエスケープシーケンスであるため，バックスラッシュを出力させるためにはもう一つバックスラッシュがその前に必要になるからです．  上記の例で，もし，バックスラッシュが一つであれば，\ と n で改行してしまいますが，2つ連続しているため，以下のように printf で出力しても \ が残っています．<jupyter_code>print(str(new_dir123))<jupyter_output>new_dir1\new_dir2\new_dir3<jupyter_text>パスの区切りは OS に依存するため，標準ライブラリの os モジュールによって柔軟に変換することができます．  パスの区切りは os モジュールの sep で参照できます．<jupyter_code>import os

os.sep<jupyter_output><empty_output><jupyter_text>もし，パスの階層の区切りを / にしたい場合は，文字列型にして replace します．<jupyter_code>str(new_dir123).replace(os.sep, "/")<jupyter_output><empty_output><jupyter_text>os モジュールも色々使い方があるのですが，今回は割愛いたします．rmdir では中にファイルやディレクトリがあると削除できませんでしたが，中にあるファイルやディレクトリごと削除したい場合は，次の shutil.rmtree を用います．  shutil shutil (https://docs.python.org/ja/3/library/shutil.html) は，ファイルの移動やコピー，圧縮／展開などの操作を行う標準ライブラリです．  このライブラリも，多くのファイル操作で役立ちます．<jupyter_code>import shutil<jupyter_output><empty_output><jupyter_text>実験する前に，今のディレクトリ構成を可視化してみます．<jupyter_code>sorted(pwd.glob("./**/*"))<jupyter_output><empty_output><jupyter_text>`test.txt` を `new_dir1` に移動してみます．  ファイルやディレクトリの移動には `shutil.move(src=移動元パス, dst=移動先パス)` を用います．<jupyter_code>shutil.move(src="./test.txt", dst="./new_dir1")
sorted(pwd.glob("./**/*"))<jupyter_output><empty_output><jupyter_text>上記の例において，引数 dst にはディレクトリパスを与えましたが，ファイルパスを与えた場合は，ファイル移動後にそのファイル名にて名前が変更されます． コピーは `shutil.copy(src=移動元パス, dst=移動先パス)` でできます．  今度は，今移動した `new_dir1/test.txt` をこの階層にコピーしてみます．<jupyter_code>shutil.copy(src="new_dir1/test.txt", dst=".")
sorted(pwd.glob("**/*"))<jupyter_output><empty_output><jupyter_text>ディレクトリの圧縮は，```shutil.make_archive(    base_name=拡張子なしの作成するファイルパス,    format=zip などの圧縮ファイルの拡張子の文字列,    root_dir=圧縮させる対象のディレクトリパス,)```でできます．  `format` は "zip" 以外にも，"tar", "gztar", "bztar", "xztar" を用いることができます．  試しに，new_dir1 以下の階層を zip で圧縮して，この階層に保存してみましょう．<jupyter_code>shutil.make_archive(
    base_name="new_dir1", format="zip", root_dir="new_dir1"
)
sorted(pwd.glob("**/*"))<jupyter_output><empty_output><jupyter_text>圧縮ファイルの展開は，```shutil.unpack_archive(    filename=圧縮ファイルパス,    extract_dir=展開するディレクトリパス)```でできます．  new_dir1.zip をここに展開すると元のフォルダと被るので，別に unpack という名前のディレクトリを作って，そこに new_dir1.zip を入れ，その中で展開してみます．<jupyter_code>Path("unpack").mkdir()
shutil.move(src="new_dir1.zip", dst="unpack/")
sorted(pwd.glob("**/*"))
shutil.unpack_archive(
    filename="unpack/new_dir1.zip", extract_dir="unpack/"
)
sorted(pwd.glob("**/*"))<jupyter_output><empty_output><jupyter_text>`shutil.rmtree(path=ディレクトリパス)` とすることにより，指定したディレクトリよりも下層のファイル／ディレクトリ含め全て根こそぎ削除することができます．<jupyter_code>shutil.rmtree(path="unpack")
sorted(pwd.glob("**/*"))<jupyter_output><empty_output><jupyter_text>time time (https://docs.python.org/ja/3/library/time.html) は，時間計測などを行う標準ライブラリです．  一定時間処理を止めたいときや処理の時間を計測するときに用います．<jupyter_code>import time<jupyter_output><empty_output><jupyter_text>一定時間処理を止めたいときは，`time.sleep(止めたい秒数)` を実行します．  秒数は，float でも指定が可能です．<jupyter_code>print("start")
for i in range(5):
    time.sleep(1)
    print(i+1)
print("end")<jupyter_output>start
1
2
3
4
5
end<jupyter_text>プログラムがスタートしてから今までの経過時間について，色んな方法があるのですが，精度などを考慮すると個人的なおすすめは，`time.perf_counter()` を用いて取得する方法です．  これにより，正確な時間が 0.1 マイクロ秒オーダーまで取得することが可能です．<jupyter_code>time.perf_counter()
time.perf_counter()<jupyter_output><empty_output><jupyter_text>プログラムスタートしてからの経過時間であるため，ある処理の塊についての時間を測定したい場合には，次のように処理の前の `tstart = time.perf_counter()` のように変数に代入しておき，経過時間算出時に `time.perf_counter() - tstart` とすることで，経過時間を取得することができます．<jupyter_code>tstart = time.perf_counter()
time.sleep(1)
telpsd = time.perf_counter() - tstart
telpsd<jupyter_output><empty_output><jupyter_text>datetime datetime (https://docs.python.org/ja/3/library/datetime.html) は，日付／時差取得などを行う標準ライブラリです．  datetime はこの中の datetime オブジェクトを用いることがほとんどですので，以下のように import してしまうことが多いです．<jupyter_code>from datetime import datetime<jupyter_output><empty_output><jupyter_text>現在の日付と時刻は `datetime.now(タイムゾーン)` にて取得できます．  引数のタイムゾーンは，初期値が None であり，この場合はシステムのタイムゾーンが適用されます．<jupyter_code>datetime.now()<jupyter_output><empty_output><jupyter_text>左から順に，年，月，日，時，分，秒，サブ秒 です．  これの type は，<jupyter_code>type(datetime.now())<jupyter_output><empty_output><jupyter_text>ということで，datetime.datetime です．  タイムゾーンを気にしなければならないケースとして，例えば，世界で個々に同時でデータを取得して，個々のシステムに依存したタイムゾーンで時間の情報を保存されているときに，どこかの時刻で統一して考えたほうが良い場合もあります．  datetime.datetime 型変数に関しては，tzinfo にてタイムゾーンがわかります（以下は，戻り値が None で出力されないため，あえて print を用いて表示しています）．<jupyter_code>print(datetime.now().tzinfo)<jupyter_output>None<jupyter_text>タイムゾーンをあわせたい場合，世界標準時（UTC）にする，または，世界標準時から何時間プラス／マイナスで書くことが多いです．  タイムゾーンを UTC に指定する場合は，datetime ライブラリの timezone オブジェクトが使えます．<jupyter_code>from datetime import timezone<jupyter_output><empty_output><jupyter_text>これを用いて UTC にしたい場合は，違いを見せるために一緒に日本の時刻も取得すると，<jupyter_code>ja_time = datetime.now()
utc_time = datetime.now(timezone.utc)

print(f"japan: {ja_time}")
print(f"utc: {utc_time}")<jupyter_output>japan: 2022-02-11 01:12:19.042634
utc: 2022-02-10 16:12:19.042634+00:00<jupyter_text>ということで，ご存知のとおり世界標準時より日本は 9 時間進んでいます．  取得時刻がシステムに影響させないようにするためには上記 timezone.utc を指定した時刻に時差分を足し引きしましょう（サードパーティライブラリの pytz を用いてタイムゾーンを指定することもできます）．datetime.datetime 型同士は引き算が可能です．<jupyter_code>tstart = datetime.now()
time.sleep(1)
telapsed = datetime.now() - tstart
telapsed<jupyter_output><empty_output><jupyter_text>このとおり，datetime.datetime 型同士の引き算は datetime.timedelta 型になります．  この時間の差分について，float の秒数で取得したくなることが多いと思うのですが，その変換は datetime.timedelta 型変数に対して total_seconds() メソッドを用いることにより取得できます．<jupyter_code>telapsed.total_seconds()
type(telapsed.total_seconds())<jupyter_output><empty_output><jupyter_text>datetime.datetime オブジェクトについてもこのまま用いるのではなく，文字列のタイムスタンプにして，例えばファイル名や時系列データの索引として用いることがあると思います．  このように，指定した文字列に変換するためには， ```datetime型変数.strftime(書式指定子)``` という風に， srtftime メソッドを用います．  引数の書式指定子は，公式ドキュメント (https://docs.python.org/ja/3/library/datetime.html) から良く使うものを以下に抜粋します． | 指定子 | 意味 | 使用例 || - | - | - || %a | ロケールの短縮系の曜日名 | Sun, Mon, ..., Sat || %A | ロケールの曜日名 | Sunday, Monday, ..., Saturday || %d | 0 埋めした 10 進数で表記した日にち | 01, 02, ..., 31 || %m | 0 埋めした 10 進数で表記した月 | 01, 02, ..., 12 || %Y | 西暦を表示 | 1900, 1901, ..., 2022, ... || %H | 0 埋めした 10 進数で表記した 24 時間表記の時間 | 00, 01, ..., 23 || %M | 0 埋めした 10 進数で表記した分 | 00, 01, ..., 59 || %S | 0 埋めした 10 進数で表記した秒 | 00, 01, ..., 59 || %f | マイクロ秒 | 000000, 000001, ..., 999999 || %Z | タイムゾーンの名前 | UTC, GMT, ... | 例えば，取得した時間を文字列のタイムスタンプとして用いることを想定すると，<jupyter_code>date_str = datetime.now().strftime("%Y%m%d%H%M%S%f")
date_str<jupyter_output><empty_output><jupyter_text>という風にでき，これを例えばファイル名などに用いることができます． 逆に， ```datetime.strptime(日付を表す文字列, 書式指定子)```  を用いることにより，文字列から datetime.datetime 型にも変換できます．<jupyter_code>datetime.strptime(date_str, "%Y%m%d%H%M%S%f")<jupyter_output><empty_output><jupyter_text>シチュエーションとしては，タイムスタンプから時間差を逆算する際に用いる場合が想定されます． 今回は，ファイル／ディレクトリ操作といったパス周りだったり，時間を扱う標準ライブラリを主に紹介しました．  他にも，乱数を扱う random だったり，数学関数を扱う math，統計関数を扱う statistics，または，csv ファイルの入出力に有益な csv といった標準ライブラリがありますが，これらを使わずに優秀なサードパーティライブラリを使うことが多いので割愛します．  上記のサードパーティライブラリについては，また他の記事に記載いたします．  演習問題 Q1. 日時を表す 2 つの文字列 `"2038/01/19-03:14:07.000000"`, `"2045/01/01-00:00:00.000000"` について時間の差を秒で出力してください．<jupyter_code>sorted(Path("./").iterdir())<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1474491110538521, 'fraction_numerical': 0.017585673795413554, 'mean_word_length': 9.648148148148149, 'pattern_counts': {'":': 0, '<': 187, '<?xml version=': 0, '>': 187, 'https://': 37, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '48', 'dir': 'jupyter-structured-clean-dedup', 'id': '301829', 'license': 'MIT', 'n_tokens_mistral': 10293, 'n_tokens_neox': 9671, 'n_words': 647, 'path': '007_std_libs/007_std_libs.ipynb', 'repo_name': 'KazutoMakino/PythonCourse', 'size': '15524'}