Lesson 9

標準ライブラリ

Lesson 9 Chapter 1
はじめに

このレッスンでは、Pythonの標準ライブラリについて学びます。これまで学んできたプログラムでは、基本的にライブラリを活用することがなかったため、今までとは異なる概念や使い方が必要になります。 標準ライブラリを利用することで、多様なタスクや処理を簡単に実装することができ、今後のプログラミングの幅が広がります。

それでは、実際に手を動かして動作を確認しながら学習していきましょう。

Lesson 9 Chapter 2
os

このチャプターではosライブラリについて学びます。主にオペレーティングシステムとのやり取りをするためのモジュールです。このライブラリを使用することで、OS上のファイルやディレクトリを管理することができます。

具体的には以下のタスクが実行できます。

  • ファイルやディレクトリの作成, 削除, リネーム
  • ファイルやディレクトリの検索
  • 環境変数の取得や変更
  • カレントディレクトリの操作

Pythonでosライブラリをインポートするには以下のように記述します。

import os

これでosライブラリを使用する準備が整いましたので、学習を進めていきましょう。

ファイルやディレクトリの作成、削除、リネーム

まず、osライブラリを使用してファイルやディレクトリを作成する方法を見ていきましょう。 osライブラリを使用すると、次のコードを使用してファイルやディレクトリを作成することができます。

import os

# ディレクトリを作成する
os.mkdir('test_dir')

# ファイルを作成する
open('test_file.txt', 'w').close()

上記のコードを実行すると、カレントディレクトリに「test_dir」というディレクトリと「test_file.txt」というファイルが作成されます。

次に、osライブラリを使用してファイルやディレクトリを削除する方法を見ていきましょう。osライブラリを使用すると、次のコードを使用してファイルやディレクトリを削除することができます。

import os

# ディレクトリを削除する
os.rmdir('test_dir')

# ファイルを削除する
os.remove('test_file.txt')

上記のコードを実行すると、カレントディレクトリから「test_dir」というディレクトリと「test_file.txt」というファイルが削除されます。

最後に、osライブラリを使用してファイルやディレクトリをリネームする方法を見ていきましょう。osライブラリを使用すると、次のコードを使用してファイルやディレクトリをリネームすることができます。

import os

# ディレクトリをリネームする
os.rename('test_dir', 'renamed_dir')

# ファイルをリネームする
os.rename('test_file.txt', 'renamed_file.txt')

上記のコードを実行すると、カレントディレクトリの「test_dir」というディレクトリが「renamed_dir」に、「test_file.txt」というファイルが「renamed_file.txt」にリネームされます。

以上が、Pythonのosライブラリを使用してファイルやディレクトリの作成、削除、リネームを行う方法でした。

ファイルやディレクトリの検索

次にosライブラリを使用して、特定のファイルやディレクトリの検索方法について学んでいきます。 まずは、検索対象となるディレクトリを指定します。

search_dir = 'C:/Users/user/Documents'

検索したいディレクトリ内のファイルやディレクトリを検索するには、os.listdir()関数を使用します。この関数は、指定したディレクトリ内のファイルやディレクトリをリスト形式で返します。

files_and_dirs = os.listdir(search_dir)

検索したいディレクトリ内の特定のファイルを検索するには、os.walk()関数を使用します。 この関数は、指定したディレクトリ内のファイルやディレクトリを再帰的に検索し、それぞれのファイルやディレクトリのパスをタプル形式で返します。

for root, dirs, files in os.walk(search_dir):
    for file in files:
        if file.endswith('.txt'):
            print(os.path.join(root, file))

上記のコードでは、指定したディレクトリ内のすべてのファイルを検索し、拡張子が「.txt」のファイルのパスを表示しています。 また、os.path.isfile()関数を使用すると、指定したパスがファイルかどうかを判定することができます。

if os.path.isfile('C:/Users/user/Documents/sample.txt'):
    print('sample.txtはファイルです')

上記のコードでは、指定したパスがファイルかどうかを判定し、ファイルであれば「sample.txtはファイルです」と表示しています。

以上のようにファイルやディレクトリの検索や、指定したディレクトリ内のファイルやディレクトリをリスト形式で取得する操作が簡単に行なえます。

環境変数の取得と変更

Pythonのosライブラリを使用すると、環境変数を取得したり変更したりすることができます。環境変数とは、システム全体で使用される変数のことです。これらの変数は、システム全体で使用される情報を保持しています。

まず、Pythonのosライブラリをインポートします。

import os

次に、環境変数を取得するために、os.environ変数を使用します。この変数は、環境変数を格納した辞書型のオブジェクトです。

env_vars = os.environ

環境変数を取得するには、辞書型のオブジェクトからキーを指定して値を取得します。例えば、PATH環境変数を取得するには、次のようにします。

path = env_vars['PATH']

環境変数を変更するには、os.environ変数に新しい値を設定します。例えば、PATH環境変数を変更するには、次のようにします。

os.environ['PATH'] = '/usr/local/bin:/usr/bin:/bin'

また、環境変数を削除するには、os.unsetenv関数を使用します。例えば、PATH環境変数を削除するには、次のようにします。

os.unsetenv('PATH')

このように環境変数の取得や変更といった、システム全体で使用される情報を操作することができます。

カレントディレクトリの操作

Pythonのosライブラリは、様々なOS(オペレーティングシステム)関連の機能を提供します。このライブラリを使用すると、OSのファイルやディレクトリを操作することができます。 今回は、Pythonのosライブラリを使用して、カレントディレクトリの操作を行う方法について詳しく説明します。

まず、カレントディレクトリとは、現在作業しているディレクトリのことです。Pythonのosライブラリを使用すると、カレントディレクトリを取得したり、変更したりすることができます。

カレントディレクトリの取得

Pythonのosライブラリを使用して、カレントディレクトリを取得するには、os.getcwd()関数を使用します。この関数は、現在のカレントディレクトリを表す文字列を返します。

import os

# 現在のカレントディレクトリを取得
current_dir = os.getcwd()

print(current_dir)
# 出力:/home/user/Documents

カレントディレクトリの変更

Pythonのosライブラリを使用して、カレントディレクトリを変更するには、os.chdir()関数を使用します。この関数は、引数として指定したディレクトリをカレントディレクトリに変更します。

import os

# 現在のカレントディレクトリを取得
current_dir = os.getcwd()

print(current_dir)
# 出力:/home/user/Documents

# カレントディレクトリを変更
os.chdir('/home/user/Downloads')

# 現在のカレントディレクトリを取得
current_dir = os.getcwd()

print(current_dir)
# 出力:/home/user/Downloads

以上が、Pythonのosライブラリを使用して、カレントディレクトリの操作を行う方法についてでした。 os.getcwd()関数を使用することで現在のカレントディレクトリを取得でき、os.chdir()関数を使用することでカレントディレクトリの変更もできます。

Lesson 9 Chapter 3
sys

sysライブラリは、Pythonインタプリタと関連する変数と関数を提供するライブラリです。 sysライブラリは、Pythonインタプリタを実行している環境に関する情報を取得したり、Pythonインタプリタを操作したりするために使用されます。

Pythonでsysライブラリをインポートするには以下のように記述します。

import sys

sysライブラリで提供される主な機能は次のとおりです。

  • sys.stdin:標準入力を取得する
  • sys.stdout:標準出力をする
  • sys.argv:コマンドライン引数を取得する
  • sys.path:モジュールを検索するパスを取得する

標準入力と標準出力

sys.stdinsys.stdoutは、Pythonプログラムから標準入力と標準出力を扱うために使用します。 基本的に、標準入力はキーボードからの入力を表し、標準出力は画面に表示する出力のことを指します。

以下は、標準入力から2つの数字を標準入力から受け付けて、それらを足し算した結果を標準出力するスクリプトです。

sample.py
import sys

def calc(a, b):
    return a + b

num1 = int(sys.stdin.readline())
num2 = int(sys.stdin.readline())

result = calc(num1, num2)

sys.stdout.write(str(result))
sys.stdout.write('\n')

出力結果

$ python sample.py
2
8
10

少し分かりにくいですがスクリプトを実行するとカーソルが点滅し、入力待ち状態になります。その状態で任意の数字を入力してEnterキーを押すと1行分の入力となります。

sys.stdin.readline()で一行分の標準入力を取得し、 sys.stdout.write()で一行分の標準出力をします。 また、上記の例ではsys.stdout.write()での出力で行末の改行がないため、最後に改行のみを出力しています。

今回の例では一行の入出力のみを紹介しましたが、一度に複数行入出力をしたり、ファイルからの入力やファイルへの出力も出来るので調べてみましょう。

モジュールを検索するパスを取得する

sys.path は、Pythonのインポート文を処理する際の検索パスを表すリストです。このリストは、Pythonのモジュールやパッケージの場所を定義します。 基本的に、このリストには、標準ライブラリのパス、現在のディレクトリの環境変数(PYTHONPATH)に設定されたパスが含まれています。

まずはsys.pathの内容を出力して確認してみましょう。

import sys

print(sys.path)

出力結果

['/Users/developer/Downloads',
'/usr/local/Cellar/python@3.10/3.10.8/Frameworks/Python.framework/Versions/3.10/lib/python310.zip',
〜省略〜
'/usr/local/lib/python3.10/site-packages']

カレントディレクトリを最初にPythonがインポートするモジュールやパッケージを探す場所が格納されています。また、このリストに新しいパスを追加することで、自分で作成したモジュールやパッケージをインポートすることもできます。

このように、sys.pathは、Pythonのモジュールやパッケージの検索パスを管理するために使用されます。

コマンドライン引数を取得する

sys.argvは、Pythonスクリプトにコマンドライン引数を渡す際に使用するリストです。このリストは、Pythonスクリプトを実行する際にコマンドラインから指定された引数を格納しています。

例えば、次のコマンドライン引数を指定してPythonスクリプトを実行すると、sys.argvには以下の出力結果の内容が格納されます。

$ python sample.py apple banana orange
sample.py
import sys

print(sys.argv)

出力結果

['script.py', 'apple', 'banana', 'orange']

sys.argv[0]には実行したPythonスクリプト名であるscript.pyが格納されています。また、 sys.argv[1]には最初に指定された引数である appleが格納されており、 sys.argvに格納された引数を使用することで、任意の入力値を受け取ることが出来るスクリプトとなります。

このようにsysライブラリはPythonインタプリタを操作することができ、標準入出力、コマンドライン引数、モジュール検索パスを取得・操作することができます。

Lesson 9 Chapter 4
glob

globライブラリはファイル名のパターンマッチングを行うためのモジュールです。指定されたパターンに合致するファイル名をリストとして生成することができ、複雑なディレクトリ構成の中で探したいファイルを瞬時に見つけることが出来ます。 例えば、以下のディレクトリ構成の中で拡張子が.txtのすべてのファイルを検索する場合、次のようになります。

┬ TEST_PROJECT
├ file_01
│  ├ test_01.txt
│  └ test_02.txt
├ main.py
└ README.txt
main.py
import glob

files = glob.glob("*.txt")
print(files)

出力結果

['README.txt']

README.txtのみ表示されていますが、検索範囲が実行ファイルである main.pyと同じ階層となっているためです。file_01配下のtxtファイルを検索する場合には以下のようになります。

import glob

files = glob.glob("file_01/*.txt")
print(files)

出力結果

['file_01/test_01.txt', 'file_01/test_02.txt']

このようにglobを使うことで、簡単にパターンマッチング検索を行うことができます。 また、先程から使用している*とは、正規表現と呼ばれる書き方の1つです。正規表現とは文字列のパターンを表現する記法で、後述する reライブラリのチャプターにて詳しく説明します。

globライブラリ自体はシンプルですが、正規表現を覚えることでより複雑な検索条件の指定をすることが出来るようになります。

Lesson 9 Chapter 5
re

reライブラリは、特定の文字列のパターンを正規表現で指定して操作する事ができるライブラリです。文字列から特定のパターンを検索したり、文字列を置換したりする際に使用します。まずは正規表現について学んでいきましょう。

正規表現とは

正規表現とは文字列のパターンを表す汎用的な記法です。一般的な英数字の文字に加えて、特殊な記号を使用することでパターンを示すことができます。 例として以下の文字列を見ていきます。

apple
banana
orange

これらの単語はすべて小文字の英字のみで構成されていることが分かります。これらの単語に当てはまるパターンを正規表現で表現すると以下のようになります。

[a-z]+

この正規表現を簡単に説明すると、a〜zのいずれかの英字を1回以上連続する文字列を意味します。今回使用した特殊文字には[]や+がありましたが、正規表現にはこれら以外の特殊文字も存在します。

特殊文字 説明
. 任意の1文字(改行以外)
* 直前の文字を0回以上繰り返す
+ 直前の文字を1回以上繰り返す
? 直前の文字が0〜1回出現
^ 文字列の先頭
$ 文字列の末尾
- 文字列の範囲の指定
[] 括弧内の任意の1文字が出現
[^] 括弧内の文字以外の任意の1文字が出現
() 複数の文字列を1つのグループとして扱う
| 左右のどちらかの文字列が出現(OR条件)
\ 次の文字をリテラルとして扱う(エスケープ処理)

このような特殊文字と英数字を使用して、様々な文字列にマッチするパターンを表現する記法が正規表現となります。

reライブラリの基本的な使用方法

まずはre.compile()関数を使って、正規表現パターンを作成します。以降のコード例では、 [0-9]+という正規表現をパターンとしています。 このパターンは「0から9までのいずれかの数字が1回以上繰り返される文字列」という意味になります。

import re

pattern = re.compile('[0-9]+')

reライブラリでは、このパターンを元に様々な文字列の検索や置換することができます。それでは、reライブラリで使用される関数について一通り学んでいきましょう。

パターンにマッチする部分をすべて取得

findall()を使用することで、正規表現で与えたパターンにマッチする部分をすべて取得し、リスト型で格納することができます。以下がコード例となります。

import re

pattern = re.compile('[0-9]+')
result = pattern.findall('こんにちは 123456 さ0よ0う0な0ら')

print(result)

出力結果

['123456', '0', '0', '0', '0']

対象外の文字が含まれている単語内でも、パターンとしてマッチする部分のみを取得していることが確認できます。

完全にパターンにマッチする文字列を取得

match()を使用することで、文字列の先頭から正規表現で与えたパターンにマッチする部分を取得することができます。以下がコード例となります。

import re

pattern = re.compile('[0-9]+')
result_1 = pattern.match('こんにちは 123456 さ0よ0う0な0ら')
result_2 = pattern.match('123a456')

print(result_1)
print(result_2)

出力結果

None
<re.Match object; span=(0, 3), match='123'>

文字列の先頭から指定したパターンにマッチする123a456という文字列のみがパターンに該当し、パターンに該当する部分までを取得していることが確認できます。 また、result_1のNoneというのは該当する文字列が存在しないことを意味します。

文字列内の最初にパターンにマッチする部分を取得

search()を使用することで、文字列内の正規表現で与えたパターンに最初にマッチする部分を取得することができます。以下がコード例となります。

import re

pattern = re.compile('[0-9]+')
result_1 = pattern.search('こんにちは 123456 さ0よ0う0な0ら')
result_2 = pattern.search('123a456')

print(result_1)
print(result_2)

出力結果

<re.Match object; span=(6, 12), match='123456'>
<re.Match object; span=(0, 3), match='123'>

match()とは違い、文字列内の最初にパターンマッチする部分のみを取得していることが確認できます。

文字列全体がパターンにマッチする文字列を取得

fullmatch()を使用することで、文字列全体が正規表現で与えたパターンにマッチする場合のみ、その文字列を取得します。以下がコード例となります。

import re

pattern = re.compile('[0-9]+')
result_1 = pattern.fullmatch('こんにちは 123456 さ0よ0う0な0ら')
result_2 = pattern.fullmatch('123a456')
result_3 = pattern.fullmatch('123456')

print(result_1)
print(result_2)
print(result_3)

出力結果

None
None
<re.Match object; span=(0, 6), match='123456'>

出力結果の通り、文字列全体がパターンマッチする文字列を取得していることが確認できます。

reライブラリでは他にも文字列の置換や分割の処理も出来ます。 reライブラリを使用することで、入力チェックといったバリデーションの実装も簡単に出来るため、正規表現の知識と共に身につけていきましょう。

Lesson 9 Chapter 6
math

mathライブラリはPythonで複雑な計算をする際に使用されるライブラリです。 具体的には対数や平方根といった四則演算のみでは難しい計算を、専用の関数を利用することで簡単に計算することが出来ます。以下のようにインポートすることでmathライブラリを使用できます。

import math

このチャプターでは数学でよく使用される演算をいくつか学習していきましょう。

平方根を求める

sqrt()関数を使用することで、引数に与えた数値の平方根を取得できます。以下はコード例となります。

import math

result_1 = math.sqrt(2)
print(result_1)

出力結果

1.4142135623730951

sqrt()関数の引数として渡した2の平方根である1.41421...が取得出来ていることが確認できます。このように平方根の計算もシンプルに求めることが出来ます。

自然対数を求める

log()関数を使用することで、引数に与えた数値の自然対数を取得出来ます。いかはコード例となります。

import math

result_1 = math.log(10)
print(result_1)

出力結果

2.302585092994046

また以下のように、引数を2つ渡すことで任意の底を指定することが出来ます。

import math

result_1 = math.log(10, 10)
print(result_1)

出力結果

1.0

このように自然対数もlog()関数を使用することで簡単に求めることが出来ます。

少数の切り上げ/切り捨て

floor()関数を使用することで浮動小数点数の最大の整数を取得できます。対して、 celi()関数を使用すると浮動小数点数以上の最小の整数を取得することも出来ます。以下がコード例になります。

import math

result_1 = math.floor(1.9)
result_2 = math.ceil(1.1)
print(result_1)
print(result_2)

出力結果

1
2

このように、複雑な計算以外にも少数切り上げ(切り捨て)といった便利な関数も用意されています。mathライブラリを使いこなすことで、関数を呼び出すだけで複雑な計算が出来ますので使いこなせるように頑張りましょう。

Lesson 9 Chapter 7
urllib.request / smtplib

urllib.requestライブラリは、URLを操作するためのライブラリです。 主にWebページを取得したり、ファイルをダウンロードしたりすることができます。以下のようにインポートすることで urllib.requestライブラリを使用できます。

import urllib.request

では実際にHTTPリクエストの送信方法から学んでいきましょう。

Webページ情報の取得

シンプルな使い方としてurlopen()関数を使用したWebページ情報の取得方法があります。以下はGoogleのホームページ情報を取得するコード例です。

import urllib.request

response = urllib.request.urlopen("https://google.com/")
html = response.read()
print(html)

出力結果

b'<!doctype html><html itemscope=""
〜省略〜
</body></html>'

このようにレスポンスの内容をread()関数を使用し出力することが出来ます。

また、Request()関数を使用することで、HTTPリクエストに情報を追加したり、ヘッダを設定したりすることができます。以下はコード例になります。

import urllib.request

url = "https://google.com/"
headers = {'parameter': 'data'}
req = urllib.request.Request(url, headers=headers)

response = urllib.request.urlopen(req)
html = response.read()
print(html)

Request()関数では主にAPIのレスポンスを確認する為に利用できます。

このようにurllib.requestライブラリは、WebスクレイピングやAPIの呼び出し、HTTPリクエストの送信、Webページの取得やデータの送信に必要な機能が備わっています。

メールの送信

smtplibライブラリはpythonでメールを送信する事ができるライブラリです。 シンプルなコードでSMTPサーバーを介してメールを送信することができます。以下はsmtplibライブラリを使用してGmailのSMTPサーバーを利用したメールを送信するコードの例です。

import smtplib

smtp_obj = smtplib.SMTP("smtp.gmail.com", 587)
smtp_obj.starttls()

smtp_obj.login("from_email@gmail.com", "password")

subject = "テストメール"
body = "こんにちは。"
msg = f"Subject: {subject}\n\n{body}"

smtp_obj.sendmail("from_email@gmail.com", "to_email@gmail.com", msg)

smtp_obj.quit()

上記のコードでは以下の手順で処理を実行しています。

  • SMTPサーバーに接続
  • SMTPサーバーにログイン
  • メールの内容を指定
  • メールの送信
  • SMTPサーバーから切断

このようにsmtplibライブラリを使用することで、簡単にemailを送信することが出来ます。

Lesson 9 Chapter 8
datetime

datetimeライブラリは、日時データを扱うためのライブラリです。日付や時間に関する様々な機能を持っています。以下のようにインポートすることで datetimeライブラリを使用することが出来ます。

import datetime

このチャプターでもよく使用される関数について学んでいきましょう。

現在の日付と時刻を取得する

datetime.now()関数を使用することで、現在の日付と時刻を取得することが出来ます。以下はコード例になります。

import datetime

now = datetime.datetime.now()
print(now)

出力結果

2023-01-28 06:42:28.393136

1つ注意点として、取得された時刻はローカル時間となっており、実行環境に設定された時刻を元に取得されます。GoogleColaboratoryで実行した場合、UTC(協定世界時)に設定されている場合があります。

日付や時間を個別に取得する

先程は現在の日付と時刻が一度に表示されましたが、特定の要素のみを指定して取得することも可能です。以下がコード例となります。

import datetime

now = datetime.datetime.now()
day = now.day
month = now.month
year = now.year
hour = now.hour
minute = now.minute

print(day)
print(month)
print(year)
print(hour)
print(minute)

出力結果

28
1
2023
6
54

このように、now()関数で取得したdatetimeオブジェクトに含まれる日時の特定の要素のみを取得できていることが確認できます。

datetimeオブジェクトを文字列へ変換

datetime.strftime()を使用することで、日時の取得以外にもフォーマットを使用して文字列として出力することも可能です。

import datetime

date_1 = datetime.datetime.now()
date_2 = date_1.strftime('%Y/%m/%d %H:%M:%S')

print(date_1, type(date_1))
print(date_2, type(date_2))

出力結果

2023-01-28 07:11:08.524587 <class 'datetime.datetime'>
2023/01/28 07:11:08 <class 'str'>

date_1にはdatetime.now()関数を使用して取得した現在の日時が格納されており、 date_2にはフォーマットを使用して任意の形で文字列として取り出していることが確認できます。

また、datetime.strptime()関数を使用することで文字列からdatetimeオブジェクトへの変換も可能です。

import datetime

date_1 = '2023-01-31 12:45:00'
date_2 = datetime.datetime.strptime(date_1, '%Y-%m-%d %H:%M:%S')

print(date_1, type(date_1))
print(date_2, type(date_2))

出力結果

2023-01-31 12:45:00 <class 'str'>
2023-01-31 12:45:00 <class 'datetime.datetime'>

引数として変換前の文字列とフォーマットを渡し、それらに従ってdatetimeオブジェクトを生成していることが確認できます。

日時の差分を計算する

最後に日時の差分を求めてみましょう。通常、文字列で定義した日時同士で演算を行うことはできません。 一方、datetimeオブジェクトでは2つの日時の差分を計算することが出来ます。以下はコード例となります。

import datetime

date_1 = datetime.datetime.strptime('2023-01-31 12:00:00', '%Y-%m-%d %H:%M:%S')
date_2 = datetime.datetime.strptime('2023-01-28 03:15:00', '%Y-%m-%d %H:%M:%S')
result = date_1 - date_2

print(result)

出力結果

3 days, 8:45:00

このように、2つの日時の差分を秒単位まで計算することができます。 datetimeライブラリを使用することで日時の取得やフォーマットを利用した整形と計算が簡単にできます。

Lesson 9 Chapter 9
zlib

zlibライブラリは、データの圧縮と解凍を行うためのライブラリです。主にファイルの圧縮やネットワーク通信でのデータ圧縮に使われます。このライブラリは、オープンソースであるzlibをPythonに移植したライブラリになります。

import zlib

text = "Apple Banana Orange"
data = zlib.compress(text.encode('utf-8'))
print(data)

出力結果

b'x\x9cs,(\xc8IUpJ\xcc\x03B\x05\xff\xa2\xc4\xbc\xf4T\x00B\xe6\x06\xd0'

compress()関数に圧縮したいテキストデータを適切な文字コードでエンコードしたデータを渡すと、圧縮されたデータが返されます。圧縮されたデータはバイナリ形式で返されます。次に圧縮されたデータを解凍してみましょう。

データの解凍

decompress()関数を使用することで圧縮データを解答することが出来ます。実際に先程圧縮したテキストデータを解凍して元に戻してみましょう。

import zlib

data = b'x\x9cs,(\xc8IUpJ\xcc\x03B\x05\xff\xa2\xc4\xbc\xf4T\x00B\xe6\x06\xd0'
text = zlib.decompress(data).decode('utf-8')
print(text)

出力結果

Apple Banana Orange

decompress()関数に解凍したいバイナリデータを渡し、解凍されたデータが返されます。解凍されたデータは、バイナリ形式で返されるため、適切な文字コードでdecodeメソッドを使って文字列に変換する必要があります。

以上がzlibライブラリの基本的な使い方となります。

Lesson 9 Chapter 10
timeit

timeitライブラリは、プログラムの実行時間を計測するためのライブラリです。コマンドラインからも実行できますが、通常はPythonコード内からインポートされて使用されます。

import timeit

code = """print("Hello!")"""

time = timeit.timeit(code, number=10)
print(time)

出力結果

Hello!
Hello!
Hello!
Hello!
Hello!
Hello!
Hello!
Hello!
Hello!
Hello!
0.00073579099989729

このようにprint()関数による出力結果が表示された後に、実行時間が秒数で表示されていることが確認できます。引数にはコードの内容と number(実行回数)を渡しています。

timeit()関数で用意されている引数は以下の通りです。

  • stmt: 計測するコードを含む文字列
  • setup: stmt実行前に実行するコード
  • timer: 実行時間を計測するために使用するタイマー関数を指定する関数
  • number: 実行回数を指定する整数値

以上がtimeitライブラリの使い方になります。非常にシンプルなライブラリですが部分的なテストをする際には便利です。

Lesson 10 Chapter 11
doctest

doctestライブラリは関数の要約(ドキュメンテーション)を書く際に、テストを同時に書くことができるライブラリです。 テストを書くことで、コードの実行例を記述することができるため、後々のメンテナンスやリファクタリング時に、意図しない動作になっていないかを確認することができます。

  1. テスト対象の関数やクラスのdocstring内に、実行例を書く
  2. `doctest.testmod()` を実行する
  3. テストの実行結果を確認する

以下はコード例となります。

import doctest

def add(a, b):
  """
  引数a, bの足し算の結果を返す関数

  >>> add(1, 2)
  3
  >>> add(10, 10)
  20
  >>> add(-1, 1)
  0
  """
  return a + b


if __name__ == "__main__":
  doctest.testmod()

出力結果

出力結果には何も表示されていませんが、これは全てのテストケースを正常に通過したことを意味します。次に、意図的に誤ったテストケースを記述して実行してみましょう。

import doctest

def add(a, b):
  """
  引数a, bの足し算の結果を返す関数

  >>> add(1, 2)
  5
  >>> add(10, 10)
  20
  >>> add(-1, 1)
  0
  """
  return a + b


if __name__ == "__main__":
  doctest.testmod()

出力結果

**********************************************************************
File "__main__", line 7, in __main__.add
Failed example:
    add(1, 2)
Expected:
    5
Got:
    3
**********************************************************************
1 items had failures:
   1 of   3 in __main__.add
***Test Failed*** 1 failures.

この出力結果から読み取れることは以下のようになります。

  • Failed example(失敗したテストケース): add(1, 2)
  • Expected(期待値): 5
  • Got(実際に得た結果): 3

このように、不具合があった箇所を具体的に出力していることが確認できます。

doctestライブラリでは入力と出力のみを比較するため、関数の詳細な動作を正確にテストすることができない場合があります。 そのため、複雑な処理を行う関数に対しては、単体テストや結合テストを併用することも必要となります。

Lesson 11 Chapter 12
unittest

unittestライブラリは、ユニットテスト(単体テスト)を行うための標準ライブラリです。 ユニットテストとはプログラムの一部分(関数やクラス)が期待通りに動くかどうかを確認するためのテスト手法でであり、unittestライブラリはユニットテストを簡単に実装することが出来ます。

ユニットテストが出来る点ではdoctestライブラリと同じですが、unittestライブラリでは豊富なアサーション(テスト用メソッド)が用意されており、より柔軟なテストを行うことができます。 今回使用するコード例では、テスト対象の関数を記述するファイルと、unittestを実行するファイルの2つを用意しています。以下がコード例となります。

test_01.py
def add(a, b):
  return a + b
test_02.py
import unittest
import test_01

class Hoge(unittest.TestCase):
    def test_add(self):
        result_1 = test_01.add(2, 3)
        self.assertEqual(result_1, 5)

        result_2 = test_01.add(10, 0)
        self.assertEqual(result_2, 10)


if __name__ == '__main__':
    unittest.main()

test_01.pyに記述されているadd関数は、2つの引数a, bの足し算の結果を返す関数となっており、test_02.pyではadd関数をインポートしてテストを実行しています。

unittestライブラリの基本的な使用手順については以下の通りです。

  1. unittestライブラリをインポートする
  2. unittest.TestCaseを継承したクラスを作成する
  3. 2で作成したクラスにtestから始まるメソッド名でを作成する
  4. アサーション関数を使用してテストを実行する

この手順でtest_02.pyを実行した結果は以下の通りです。

.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

assertEqual()関数では、与えた2つの引数の値が一致するかどうかを判定します。 また、破線の上部に表示されている.が、1つのテストを完了し正常に通過したことを意味します。テストはアサーション単位ではなく、メソッド単位で行われる点に注意しましょう。

次にtest_02.pyを修正し、意図的に誤ったアサーションを記述して実行してみます。

test_02.py
import unittest
import test_01

class Hoge(unittest.TestCase):
    def test_add(self):
        result_1 = test_01.add(2, 3)
        self.assertEqual(result_1, 0)

        result_2 = test_01.add(10, 0)
        self.assertEqual(result_2, 0)


if __name__ == '__main__':
    unittest.main()

出力結果

F
======================================================================
FAIL: test_add (__main__.Hoge)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/developer/Downloads/test_project/test_02.py", line 7, in test_add
    self.assertEqual(result_1, 1)
AssertionError: 5 != 1

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (failures=1)

このように最初にエラーとなった箇所でテストが中断し、その箇所が出力されています。また、先程は.と表記されていた箇所に、テストに失敗したことを意味する Fが表示されていることも確認できます。

アサーション一覧

次にunittestライブラリで使用されるアサーションについて学んでいきましょう。関数の種類としては以下の通りです。

アサーション 説明
assertEqual(a, b) aとbが等しいかどうかを確認する
assertNotEqual(a, b) aとbが等しくないかどうかを確認する
assertTrue(x) xがTrueであるかどうかを確認する
assertFalse(x) xがFalseであるかどうかを確認する
assertIs(a, b) aとbが同じオブジェクトかどうかを確認する
assertIsNot(a, b) aとbが異なるオブジェクトかどうかを確認する
assertIsNone(x) xがNoneであるかどうかを確認する
assertIsNotNone(x) xがNoneでないことを確認する
assertIn(a, b) aがbに含まれているかどうかを確認する
assertNotIn(a, b) aがbに含まれていないことを確認する
assertIsInstance(a, b) aがbのインスタンスであるかどうかを確認する
assertNotIsInstance(a, b) aがbのインスタンスでないことを確認する

これらのアサーションを使用して様々なテストケースを実装することが出来ます。

以上がunittestライブラリの使い方になります。

Lesson 12 Chapter 13
locale

localeライブラリは、地域設定に関するデータを管理するためのライブラリです。 地域設定によって、日付、通貨、数字が表示される形式を変更することが出来ます。これにより、プログラムの出力結果を地域設定に合わせた形式で表示することができます。

import locale

locale.setlocale(locale.LC_ALL, 'ja_JP.UTF-8')

上記のコード例では、LC_ALLという変数に対して地域設定をja_JP.UTF-8に変更しています。この設定により、日付や通貨、数字の表示形式が設定された地域に適切な形となります。

使用できるロケールの確認

先程はja_JP.UTF-8というロケールを選択しました。これら以外にもロケールは存在しており、実行環境によって使用できるロケールが異なります。使用できるロケールを確認するには以下のコマンドを実行します。

locale -a

出力結果

en_NZ
nl_NL.UTF-8
pt_BR.UTF-8
〜省略〜
tr_TR
C
POSIX

このように、実行環境下でサポートされているロケールが確認できます。基本的にはこのリストの中からロケールを選択していきます。

通貨の表示

以下のコード例では、日本語の通貨を表示しています。


import locale

locale.setlocale(locale.LC_ALL, 'ja_JP.UTF-8')
currency = 12345.67
print(locale.currency(currency, grouping=True))

出力結果

¥12,346

このように日本円の記号を含めた表記になっていることが確認できます。また、grouping=Trueと指定することで、桁区切りを有効にすることができます。

日時の表示

最後にdatetimeライブラリと併用して、今日の日付を指定した地域ごとに表示を切り替える流れを見ていきましょう。以下がコード例となります。

import datetime
import locale

# ロケールを日本に変更し、現在日時を取得する
locale.setlocale(locale.LC_TIME, 'ja_JP.UTF-8')
today = datetime.date.today()

print(today.strftime('%a'))
print(today.strftime('%A'))

# ロケールをアメリカに変更
locale.setlocale(locale.LC_TIME, 'en_US.UTF-8')

print(today.strftime('%a'))
print(today.strftime('%A'))

出力結果

金
金曜日
Fri
Friday

このように、各ロケールに沿った現在日時の表示形式で出力されます。以上がlocaleライブラリの使い方となります。

Lesson 13 Chapter 14
logging

loggingライブラリは、アプリケーションの実行ログを記録するためのライブラリです。主にファイル、コンソール、メール、TCPソケットといった様々な出力先にログを送信することができます。 例えば、アプリケーションが想定通りに動作していない場合、ログを確認することで問題箇所を特定することができます。また、アプリケーションの性能を監視するために、ログに測定値を出力することもできます。

loggingライブラリを使用するために必要となる設定と流れは以下のようになります。

  1. loggingインスタンスの作成
  2. ログレベルの設定
  3. ログ出力先の設定

これらを分けて説明していきます。

loggingインスタンスの作成

まずは、loggingインスタンスを作成します。このインスタンスは、ログの出力先を指定したり、ログレベルの設定のために使用されます。 loggingインスタンスは、モジュールごとに作成することが一般的です。以下はコード例となります。

import logging

logger = logging.getLogger(__name__)

上記の例では、現在メインとして実行しているスクリプト(__name__)をターゲットにログの収集を行います。

ログレベルの設定

先程作成したインスタンスに対してsetLevel()関数を使用することで、ログレベルの設定をすることが出来ます。ログレベルには重要度低い順から、DEBUG、INFO、WARNING、ERROR、CRITICALの5つのレベルがあります。 ログレベルはログの重要度に応じて選択する必要があり、例えば、デバッグ情報をログに出力する場合はDEBUGレベルを選択します。

logger.setLevel(logging.DEBUG)

上記の例では、DEBUGレベル以上のログが出力される設定となります。

ログ出力先の設定

最後に、ログの出力先を設定します。loggingライブラリは、様々な出力先にログを送信することができますが、今回はファイルにログを出力する設定とします。

file_handler = logging.FileHandler('example.log')
logger.addHandler(file_handler)

これでロガーの設定が完了しました。実際にこのロガーを使ってログを出力してみましょう。以下はログレベルWARMING以上のログ出力のみを記録するプログラムの例です。

import logging

logger = logging.getLogger(__name__)
logger.setLevel(logging.WARNING)
file_handler = logging.FileHandler('example.log')
logger.addHandler(file_handler)

if __name__ == '__main__':
    logger.debug('This is a debug message.')
    logger.info('This is an informational message.')
    logger.warning('This is a warning message.')
    logger.error('This is an error message.')
    logger.critical('This is a critical message.')

出力結果

出力結果には何も表示されていませんが、example.logファイルを確認してみましょう。

This is a warning message.
This is an error message.
This is a critical message.

このように、設定したログレベル以上のログ出力のみ記録していることが確認できます。このように、loggingライブラリを使用することで、アプリケーションの実行ログを簡単に記録することができます。

Lesson 14 Chapter 15
decimal

decimalライブラリは、高精度な10進数演算を行うためのライブラリであり、正確な小数点以下の桁数を扱うことができます。 Pythonには標準で浮動小数点数を扱うためのfloatが用意されていますが、精度の問題があるため正確な算術演算を必要とする場合には適さないといったデメリットがあります。そういった場合に decimalライブラリを利用します。

float型との違い

実際に浮動小数点数の計算で、float型とdecimalライブラリを使用した際の比較をしていきます。以下はコード例となります。

import decimal

a = 0.1
b = 0.2
print(a + b)

a = decimal.Decimal('0.1')
b = decimal.Decimal('0.2')
print(a + b)

出力結果

0.30000000000000004
0.3

このように同じ浮動小数点数の足し算ですが、float型の結果には僅かな誤差がある一方でdecimalライブラリを使用した演算では全く誤差がないことが確認できます。

注意点としてDecimal()クラスに渡す値は文字列でなければなりません。数値を渡した場合にエラーは発生しませんが、float型の計算時と同様に誤差が発生する原因となります。

精度の指定

decimalライブラリはデフォルトでは、小数点以下28桁までの精度に設定されています。 この精度を変更するにはgetcontext()関数を使用して、Contextオブジェクトを取得し、そのprec属性に新しい精度を設定します。

import decimal

context = decimal.getcontext()
context.prec = 50

a = decimal.Decimal(0.1)
b = decimal.Decimal(0.2)
c = a + b
print(c)

出力結果

0.30000000000000001665334536937734810635447502136230

ここでは敢えてDecimal()クラスに数値を渡して誤差を出しています。これにより、計算結果の小数点以下50桁が表示されていることを確認できます。

以上が、decimalライブラリの概要と使い方でした。floatと比較して decimalは精度が高いため、正確な算術演算を必要とする場合に利用されます。

ここまでPythonの標準ライブラリについて学んできました。初めは新しい概念や使い方に戸惑うかもしれませんが、何度も繰り返し見直したり実際にコードを書きながら進めていくことで徐々に理解が深まり、 複雑な処理もより簡単に実行できるようになることで、冗長なコードもより見やすくすることが出来ます。このレッスンの内容を元にPythonの標準ライブラリを理解し、より高度なプログラミングスキルを身につけていきましょう。