9. 標準ライブラリ

pythonで利用できる関数やメソッドは、必要最小限のもしかありません。標準 以外の関数やメソッドを利用するために、モジュール(またはライブラリ呼ば れる)を追加して利用できるようにします。モジュールは、目的とする機能毎 に分類されており、import文で必要なモジュールをプログラムに組み込んで利 用します。クラスと関数が一式になっているものがモジュールと考えれば良い でしょう。

世の中には、非常にたくさんのモジュールが存在しますが、Pythonと一緒に配 布されているものを標準ライブラリと呼びます。標準ライブラリ以外のライブ ラリを外部(third party)ライブラリと呼びます。第12回講義の目的は以下の とおりです。

  • モジュール(標準ライブラリ)のインポートの方法

  • 代表的なライブラリの利用方法

Webプログラムやデータベース等の多数の標準ライブラリが利用できますが、 ここでは、利用頻度の高い標準ライブラリを紹介します。その他の標準ライブ ラリは、リンク先を参照してください。 https://docs.python.org/ja/3.5/library/index.html

プログラムにモジュールを読み込むために、プログラムの先頭で以下の書式を使います。

example-01
import module_name                       # 特定のmoduleを読み込む
import module_name as alias              # 別名aliasでmoduleを読み込む

注釈

プログラムを自作する際、既に存在するライブラリの機能を自分で実装する ことは無駄以外の何物でもありません。すでに存在するライブラリを存分に 使ってプログラムを作成した方が効率的ですし、間違いも少なくなります。 勉強目的の場合を除いて決して車輪の再発明を行わないようしてください。 標準で組み込まれているライブラリ以外にも多数のライブラリが世の中に存 在します。後述するnumpyやscipy、matplotlibは、標準のpythonに含まれま せんが科学技術計算で有名なライブラリです。これらライブラリを理解して 使いこなすこともプログラミングの重要な要素です。

sys

sysモジュールで、コマンドプロンプトからsys.stdinを経由した文字の取得や 出力バッファの制御、引数の取得等が可能になります。詳細はマニュアル (https://docs.python.jp/3.7/library/sys.html) を参照してもらえればと思 いますが、コマンドラインからの入出力と引数の取得だけ取り扱います。

コマンドラインからpythonプログラムの実行は以下のように行います。ここで、 arg1 arg2 ...は引数で、sysモジュールを使ってプログラム中から参照できま す(example-03)。

python program_name arg1 arg2 ...

注釈

一般のソフトウェアのインターフェースは、マウスでポチポチ操作する GUI(Graphical User Interface)です。一方、キーボード入力と画面の文字表 示のみでコンピュータを操作するインターフェースはCUI(Character User Interface)と言います。GUIは絵や文字を使って操作できるため、使いやすい 面がありますが、科学技術計算で大量のデータを取り扱う場合や複雑な処理 を連続して行うことに向いていません。また、GUIはグラフィクスを描画する 必要があるため、無駄に計算機のリソースを使います(その分、計算は遅くな る)。一般の人が使いやすいと思うソフトウェアを開発するためにはGUIが必 須ですが、計算機の性能をフルに発揮させたい科学技術計算では、CUIがよく 使われます。

コマンドラインからプログラムを実行して対話式にデータを読み込み出力する プログラムを作ってみます。CUIでの操作は、 コマンドプロンプトによ る操作 を参照してください。コマンドプ ロンプトを立ち上げてプログラムを実行してみます。

プログラムの標準入力は、ファイル記述子のsys.stdinを使います。sys.stdin にreadline()メソッドを摘要すれば、1行文のデータを取得できます。print() の結果を即時出力させるために、sys.stdout.flush()を使います。

example-02
1import sys                         # sysモジュールをimport
2
3i = sys.stdin                      # インプット用のファイル記述子の定義
4o = sys.stdout                     # アウトプット用のファイル記述子の定義
5
6print("input anything: ", end="")  # 入力プロンプト
7o.flush()                          # 出力bufferを吐き出して"input anything: "を表示
8l = i.readline()                   # 1行を読み込み, 入力待ちになる
9print(l, "type :", type(l))

コマンドラインからプログラムを実行する場合、引数をsys.argv(リスト変数) として受け取ることができます。

example-03
1import sys                         # sysモジュールをimport
2
3# python example-02.py a b c d でプログラムを実行する。
4print("arg1:", sys.argv[0]) # example-02.py
5print("arg2:", sys.argv[1]) # a
6print("arg3:", sys.argv[2]) # b
7print("arg4:", sys.argv[3]) # c
8print("arg5:", sys.argv[4]) # d

argparse

引数制御をより詳細に取り扱うためのモジュールです。少し細かい引数制御を 行いたい場合、sys.argvではなく、argparseを使った方が良いしょう。

https://docs.python.jp/3.7/library/argparse.html

sys.argvで引数を受け取る場合、引数の型や数を自分で管理せねばならず、余 分にコードを書かねばなりません。argparseを使ってテンプレートに基づいた 引数の受け取りを定義することで、エラー処理や引数処理を効率的に行うこと ができます。

example-04
 1import argparse                         # argparseモジュールをimport
 2
 3# 引数解析のためのインスタンス作成
 4par = argparse.ArgumentParser(description="test")
 5par.add_argument('-f', '--files')
 6par.add_argument('-d', '--df', default="hoge", help="help表示の文字列")
 7par.add_argument('-i', '--int', help="引数を1つとる", type=int)
 8par.add_argument('-n', '--num', nargs=2,  help='引数を複数とる')
 9par.add_argument('-m', '--mat', nargs=2,  required=True,
10                 help='引数は必ず指定する。')
11par.add_argument('-c', '--choice', choices=["a", "b", "c"],
12                 help='a, b, cの中から選ばせる')
13
14# 引数解析を実行, '--xxxで与えた変数はargs.xxxでアクセス'
15args = par.parse_args()
16
17# 取得した引数のアクセス
18print(args.df)

os.path

ファイルの有無(os.path.exists())を確認したり、ファイルパスとファイル名を分割 (os.splitext())する等の機能を含むモジュールです。拡張子とファイル名前 を分離したり、ファイルの存在の有無を確認することができます。

https://docs.python.jp/3.7/library/os.path.html

引数で与えられたファイルの存在を確認するプログラムを作成してみます。

example-05
 1import os.path           # os.pathをimport
 2import sys               # sysをimport
 3
 4# 引数の文字列を取得
 5fname = sys.argv[1]
 6# ファイルの有無を確認
 7
 8if os.path.exists(fname) is True:
 9    print("{} is existing".format(fname))
10else :
11    print("{} is not existing".format(fname))
12    exit()
13
14# basenameとextensionを分離する
15base, ext = os.path.splitext(fname)
16print("basename: {}".format(base))
17print("extension: {}".format(ext))

glob

https://docs.python.jp/3.7/library/glob.html

複数のファイルを一括して処理したい場合、ファイル名をパターンで指示する ことで、ファイルリストを得ることができます。

example-06
1import glob
2
3# 拡張子がxyzのファイル全てをfilesにリストする
4files = glob.glob("*.xyz")
5
6# ファイルリストを出力する。
7for i, f in enumerate(files):
8    print("{}: {}".format(i+1, f))

math (numpyで代用可)

sinやcos, log等の数学関数を扱うためのモジュールです。mathで扱うすべて 関数やメソッドは、後述するnumpyで代用できます。numpyをimportする場合、 あえてmathをimportする必要はないのですが、ここでは少しだけmathの機能に 触れることにします。 https://docs.python.jp/3.7/library/math.html

馴染みのある数学関数をあげています。sin(), cos()等を計算する場合、角度 は全てradianで与えることい注意してください。radianとdegree(度)の変換は 以下のとおり。

\[ \begin{align}\begin{aligned}&\theta ({\rm radian}) = \theta ({\rm degree}) \times \pi/180\\&\theta ({\rm degree}) = \theta ({\rm radian}) \times 180/\pi\end{aligned}\end{align} \]
数学関数一覧

メソッド

機能

math.sin()

\(\sin(\theta)\) の計算。xはラジアンで与えることに注意。

math.cos()

\(\cos(\theta)\) の計算。xはラジアンで与えることに注意。

math.tan()

\(\tan(\theta)\) の計算。xはラジアンで与えることに注意。

math.acos()

\(\cos^{-1}(x)\) の計算。返り値はラジアンになることに注意。

math.asin()

\(\sin^{-1}(x)\) の計算。返り値はラジアンになることに注意。

math.atan()

\(\tan^{-1}(x)\) の計算。返り値はラジアンになることに注意。

math.log()

自然対数 \(\log(x)\) の計算。

math.exp()

指数対数 \(\exp(x)\) の計算。

math.sqrt()

平方根 \(\sqrt{x}\) の計算。

math.log10()

常用対数(底が10) \(\log(x)\) の計算。

数学関数を使った例を示します。

example-07
 1import math
 2
 3# パイの出力
 4import math
 5
 6print("pi = {:6f}".format(math.pi))  # パイ 3.14159...
 7print("e = {:6f}".format(math.e))    # 自然対数の底 2.71828...
 8print("sin(180) = {:6f}".format(math.sin(math.pi)))  # 0.0
 9print("cos(30) = {:6f}".format(math.cos(math.pi/6))) # 0.866..
10print("exp(0.1) = {:6f}".format(math.exp(0.1)))      # 1.105..
11print("log10(0.1) = {:6f}".format(math.log10(0.1)))  # -1
12
13# 逆関数, radianで値を返すので注意, math.degrees()で変換しても同じ
14c = math.sqrt(3)/2
15print("cos-1(sqrt(3)/2) = {:6f}".format(math.acos(c) * 180/math.pi))

数学関数以外のメソッドを4つだけ挙げます。

数学関数以外のメソッド

メソッド

機能

math.floor()

引数で実数を与えて、実数の切り捨て値(床)を返す。(1.2や1.9ならば1, -0.9ならば-1)

math.ceil()

引数で実数を与えて、実数の切り上げ値(天井)を返す。(1.2や1.9ならば2, -0.9ならば0)

math.degrees()

radianをdegreeに変換した結果を返す。

math.radian()

degreeをradianに変換した結果を返す。

floor()とceil()関数の例を示します。

example-08
 1# math.floor()の確認
 2print(math.floor(-2.9))  # -3
 3print(math.floor(-2.1))  # -3
 4print(math.floor(-0.9))  # -1
 5print(math.floor(0.1))   # 0
 6print(math.floor(1.2))   # 1
 7print(math.floor(1.9))   # 1
 8
 9# math.ceil()の確認
10print(math.ceil(-2.9))   # -2
11print(math.ceil(-2.1))   # -2
12print(math.ceil(-0.9))   # 0
13print(math.ceil(0.1))    # 1
14print(math.ceil(1.2))    # 2
15print(math.ceil(1.9))    # 2

random (numpyで代用可)

擬似乱数(pseudo random number)を生成します。乱数とは、デタラメな数字の ことです。乱数を使って1から6までのデタラメな数字を吐き出すことができれ ば、コンピュータにサイコロを振らせることを考えます。乱数を利用するため に、randomモジュールを使います。擬似乱数の発生もnumpyで代用できます。

https://docs.python.jp/3.7/library/random.html

乱数を生成するために、seedを与えます。同じseedを与えれば、生成される乱 数は、同じになります。seedを与えなければ、実行した日付と時刻からseedを 与える(いつもunique)ので、常に異なる乱数が生成されます。

math.floor()と合わせてサイコロを10回ふる実験を行ってみます。

example-09
 1import random    # randomモジュールをimport
 2import math      # mathモジュールをimport
 3
 4random.seed(10)  # seedとして10を与える。
 5for i in range(10):
 6    n = random.random()              # nは 0から1までの実数 [0, 6)
 7    n = n * 6                        # nは 0から6までの実数 [0, 6)
 8    p = math.floor(n)                # pは0, 1, 2, 3, 4, 5のどれか
 9    s = "{}回の目 = {}"              # 表示用の文字列
10    print(s.format(i, p+1))          # 出力

re

正規表現とは、文字列の集合を一つの文字列で表現する方法です。文字列を柔 軟に選択したり置換することができます。任意の文字列を表現する メタキャ ラクタ文字 、文字列の繰り返しを指定する 量指定子 、文字列の場所 を指定する アンカー指定子 を組み合わせて柔軟に文字列の集合を表現す ることができます。詳細はリンクを参照してください。 https://docs.python.jp/3.7/library/re.html

メタキャラクタ文字の一覧

メタキャラクタ

意味

.

任意の1文字

w

英単語を構成する文字(a~z,A~Z,_,1~9)

W

英単語を構成する文字以外

s

空白文字(半角スペース,タブ,改行,キャリッジリターン)

S

空白文字以外

d

半角数字(0-9)

D

半角数字以外

[xyz]

x, y, zのどれかに一致

[a-z]

マッチする文字の範囲を指定する表現(aからzのどれか)

量指定子一覧

量指定子

意味

*

0回以上の繰り返し

+

1回以上の繰り返し

{n}

n回の繰り返し

{n,}

n回以上の繰り返し

{n,m}

n回以上、m回未満の繰り返し

?

0回または1回の繰り返し

アンカー指定子

アンカー指定子

意味

^

文字列の先頭

$

文字列の末尾

正規表現を使って、文字列を置換したり抜き出したりしてみます。

example-10
 1import re
 2
 3s = """Chiba University (千葉大学 Chiba Daigaku) and it is also
 4abbreviated as Chibadai (千葉大) is a national university in the city
 5of Chiba, Japan. It offers Doctoral degrees in education as part of a
 6coalition with Tokyo Gakugei University, Saitama University, and
 7Yokohama National University. The university was formed in 1949 from
 8existing educational institutions in Chiba Prefecture, and absorbed
 9over a period of years Chiba Medical University (1923-1960), a
10preparatory department of the Tokyo Medical and Dental University,
11Chiba Normal School (1872-1951), Tokyo Polytechnic High School
12(1914-1951), Chiba Horticultural High School, and others. Chiba
13University was reincorporated in 2010 under the National University
14Corporation Act.[1][2] Chiba University has been ranked 75th on the
15Asia University Rankings 2013 Top 100 by "The Times Higher Education".
16"""
17
18lines = s.split("\n")
19
20# ***[uU]niversityだけを表示
21for i, l in enumerate(lines):
22    o = re.search("(\S+?) [uU]niversity", l)
23    if o is not None:
24        print("{}: {}".format(i + 1, o.group(0)))
25
26# [uU]niversityをPRISONに置換
27for i, l in enumerate(lines):
28    lines[i] = re.sub("(\S+?) [uU]niversity", "\\1 PRISON", l)
29new_s = "".join(lines)
30print(new_s)
31
32# xxxx年のリストを取得
33years = re.findall("[12]\d{3}", s)
34for y in years:
35    print(y)

urllib

web上のデータをPythonでダウンロードするためのモジュールです。ここでは、 web上のURLを開いて読むためのモジュールであるurllib.requestのみ扱います。 通常ファイルを開く時に、関数open()でファイル識別子を生成して、read()で ファイルの内容を読み込んだと思います。

urlopen()を使って、通常ファイルと同じようにファイル識別子を作成し、 read()でウェブ上のデータを読みこむことができます。読み込んだデータは、 バイトオブジェクトになるので、文字型に変更します。

https://docs.python.org/ja/3.7/library/urllib.request.html#module-urllib.request

以下のURLにあるデータを読みこんで、文字型に変更し内容を表示または別 名で保存するプログラム例です。

http://chem.tf.chiba-u.jp/gacb10/lecture.files/chem_computer/files/data.csv

example-11
 1import urllib.request
 2
 3host = "http://chem.tf.chiba-u.jp"
 4path = "/gacb10/lecture.files/chem_computer/files/data.csv"
 5url = host + path
 6
 7o = urllib.request.urlopen(url)  # ファイル識別子の生成
 8body = o.read()   # web上のdata.csvを読み込み, bodyはバイトオブジェクト
 9body = body.decode('utf-8')  # .decode()メソッドで文字型に変換
10lines = body.split("\n")    # 1行1要素のデータに分割
11print(lines[2])  # 3行目のデータを表示
12# web上のdata.csvをhoge.dataとして保存
13o = open("hoge.data", "w")
14o.write(body)

クイズ

Q1

以下のプログラムを改造して、おみくじプログラムを完成させる。プログラム を実行した後、Returnを押すとおみくじの結果を表示させます。自分で使い方 を調べてrandom.choice()を使っても良いです(重要なこと)。

 1import sys       # sysモジュールをimport
 2import random    # randomモジュールをimport
 3import math      # mathモジュールをimport
 4
 5omikuji = ["大吉: 2018年度は明るい。お金も待ち人もザクザク。",
 6           "中吉: 2018年度はまずます。まずまず幸せでしょう。",
 7           "小吉: 2018年度はそこそこ。まぁ普通かな。",
 8           "吉:   2018年度は普通。何事もないでしょう。",
 9           "凶:   2018年度は不幸。家でじっとしておこう。",
10           "大凶: 2018年度は絶望的。進級もあぶないぞ!"]
11
12print("おみくじを引くために、Returnを押してください。")

Q2

水分子のxyzファイル を読んでH-O-Hの角度 \(\theta\) を計算する。求めた \(\theta\) は度(degree)で表示すること。なお、ファイルをopenする前に 水分子のxyzファイル の有無をos.path.exists()で確認する。H-O-Hの角度は、原子座標のベクトルより以下の内積の公式を使って計算できる。

\[ \begin{align}\begin{aligned}\vec{a}\cdot \vec{b} &= |\vec{a}||\vec{b}|\cos\theta\\\theta & = \cos^{-1}\left( \frac{\vec{a}\cdot \vec{b}}{|\vec{a}||\vec{b}|} \right)\end{aligned}\end{align} \]
../_images/water.png

答え

 1import os
 2import math      # mathモジュールをimport
 3
 4xyzfile = "water.xyz"
 5
 6if os.path.exists(xyzfile):
 7    pass
 8else:
 9    print("water.xyz is not found.")
10    exit()
11
12lines = open(xyzfile).readlines()
13O = [float(v) for v in lines[2].split()[1:]]
14H1 = [float(v) for v in lines[3].split()[1:]]
15H2 = [float(v) for v in lines[4].split()[1:]]
16v1 = [H1[0] - O[0], H1[1] - O[1], H1[2] - O[2]]    
17v2 = [H2[0] - O[0], H2[1] - O[1], H2[2] - O[2]]
18v1_ = math.sqrt(v1[0]**2 + v1[1]**2 + v1[2]**2)
19v2_ = math.sqrt(v2[0]**2 + v2[1]**2 + v2[2]**2)
20term1 = v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]
21term2 = v1_ * v2_
22theta = math.acos(term1/term2)
23print("theta = {:.2f} deg.".format(theta*180/math.pi))

Q3

ユーザーと10回じゃんけんをし、勝ち、負け、引き分けの数を表示するプログラムを作成する。なおグー、チョキ、パーは、sys.stdinでユーザーにグー(a)、チョキ(b)、パー(c)として入力させ、sys.stdinで読み取る。

答え

 1import sys
 2import random
 3
 4win, even, lose = 0, 0, 0
 5
 6c = ["a", "b", "c"]
 7prompt = "a(グー), b(チョキ), c(パー)のいずれかを入力してください。"
 8d = {"a": "グー", "b": "チョキ", "c": "パー"}
 9r = {0: "引き分け", 1: "あなたの勝ち", 2: "あなたの負け"}
10
11i = 0
12while(i < 10):
13    print("{}: {}".format(i + 1, prompt))
14    sys.stdout.flush()
15    s = sys.stdin.readline().strip()   # 人間の手出し
16    u = random.choice(c)               # 乱数で決めた手出し
17    if s == u: # 引き分けの場合
18        flag = 0
19    elif s == "a" and u == "b":
20        flag = 1
21    elif s == "a" and u == "c":
22        flag = 2
23    elif s == "b" and u == "a":
24        flag = 2
25    elif s == "b" and u == "c":
26        flag = 1
27    elif s == "c" and u == "a":
28        flag = 1
29    elif s == "c" and u == "b":
30        flag = 2
31    else:
32        print("a b cいずれかを入力してください。 ")
33        continue
34    print("あなた:{} 相手:{} 結果{}".format(d[s], d[u], r[flag]))
35    if flag == 0:
36        even += 1
37    if flag == 1:
38        win += 1
39    if flag == 2:
40        lose += 1
41    i = i + 1
42
43print("引き分け数: {}".format(even))
44print("あなたの勝ち数: {}".format(win))
45print("あなたの負け数: {}".format(lose))

Q4

以下のURLにある株情報が記載されたstock.csvをimport urllib.requestを用 いてダウンロードする(stock.csvはcp932でエンコードされています)。

url
http://chem.tf.chiba-u.jp/gacb10/lecture.files/chem_computer/files/stock.csv

さらにダウンロードしたファイルを読んで、始値と終値の変化量を求め、変化量が400円以上500円未満の日付を表示する。

答え

 1import urllib.request
 2
 3url = "http://chem.tf.chiba-u.jp/gacb10/lecture.files/chem_computer/files/stock.csv"
 4o = urllib.request.urlopen(url)  # ファイル識別子の生成
 5body = o.read().decode('cp932')
 6lines = body.strip().split("\n")
 7for line in lines[2:]:
 8    line = line.replace('"', '')
 9    d = line.split(",")
10    date = d[0]
11    start = float(d[1])
12    end = float(d[4])
13    delta = start - end
14    if 400 <= delta and delta < 500:
15        print(delta, date)