ニュートン法
def a(x): return (x**2 + 2) / (2 * x) x = 2 # 初期値(0以外の値) for i in range(6): x = a(x) print(x) # 1.5 # 1.4166666666666667 # 1.4142156862745099 # 1.4142135623746899 # 1.414213562373095 # 1.414213562373095
ニュートン法は接線を利用することで解の近似を行っている。
続いて√3
def a(x): return (x**2 + 3) / (2 * x) x = 2 # 初期値(0以外の値) for i in range(8): x = a(x) print(x) # 1.75 # 1.7321428571428572 # 1.7320508100147274 # 1.7320508075688772 # 1.7320508075688774 # 1.7320508075688772 # 1.7320508075688774 # 1.7320508075688772
誤差のせいか収束しないので、decimalを
from decimal import Decimal def a(x): return Decimal(Decimal(x)**2 + s) / (2 * Decimal(x)) s = 3 # √s x = 2 # 初期値(0以外の値) for i in range(8): x = a(x) print(x) # 1.75 # 1.732142857142857142857142857 # 1.732050810014727540500736377 # 1.732050807568877295254353946 # 1.732050807568877293527446342 # 1.732050807568877293527446342 # 1.732050807568877293527446342 # 1.732050807568877293527446342
収束おっけ
一般的に、x(new)=x-f(x)/f'(x)を繰り返すとf(x)=0の解の1つが得られる。
# y=f(x) def f(x): return x**2 - 2 # 導関数 def diff(x): return (f(x + 0.001) - f(x)) / 0.001 # ニュートン法の漸化式 def a(x): return x - f(x) / diff(x) x = 2 # 初期値 while f(x) > 10**-10: # 誤差が10**-10になるまで繰り返す x = a(x) print(round(x, 10)) # 1.5001249688 # 1.4167014195 # 1.4142166238 # 1.4142135635 # 1.4142135624
解が複数あったり、あるいは解がなかったりのときは何か工夫しないと。
matplotlibで描いたグラフのコード
def f(x): return x**2 - 2 # 導関数 def diff(x): return (f(x + 0.001) - f(x - 0.001)) / 0.002 # x=3 における接線の方程式 def tangent(x): return diff(3) * (x - 3) + f(3) x = np.linspace(-2, 4, 50) y = f(x) plt.plot(x, y) ax = plt.gca() ax.spines['top'].set_position(("data",0)) ax.spines['right'].set_color('none') ax.spines['bottom'].set_color('none') ax.xaxis.set_ticks_position("top") fn = "Times New Roman" plt.plot([3, 3], [2, f(3)], "k:") plt.plot([1, 4], [tangent(1), tangent(4)], color="r") plt.text(2.9, -1, "$x_0$") plt.text(1.8, -1, "$x_1$") plt.title("$y=x^2-2$") plt.xlim(-2, 4) plt.show()
ビット操作
pythonのビット操作について
ビットand, or, xor, not
& :ビットand | :ビットor ^ :ビットxor ~ :ビットnot(反転)
print(bin(0b0101 & 0b1100)) # 0b100 print(5 & 12) # 4 print(bin(0b0101 | 0b1100)) # 0b1101 print(5 | 12) # 13 print(bin(0b0101 ^ 0b1100)) # 0b1001 print(5 ^ 12) # 9 print(bin(~0b0101)) # -0b110 print(~5) # -6
ビットシフト
>> :右シフト << :左シフト
print(bin(0b111 << 2)) # 0b11100 print(7 << 2) # 28 print(bin(0b111 >> 2)) # 0b1 print(7 >> 2) # 1
右シフトはpow(2, n)での除算、左シフトはpow(2, n)の乗算にあたる。
以上はすべて、全ビット対象。
特定のビットの操作
下からn桁目のbitを取り出す | A & pow(2, n) |
下からn桁目のbitのみを1にする | A | pow(2, n) |
下からn桁目のbitのみを0にする | A & ~pow(2, n) |
下からn桁目のbitを反転する | A & ~pow(2, n) | ~(A & pow(2, n)) |
反転は他にいい方法ありそう
print(bin(0b1111 & pow(2, 1))) # 0b10 print(bin(0b0000 | pow(2, 3))) # 0b1000 print(bin(0b1111 & ~pow(2, 1))) # 0b1011 print(bin(0b1111 & ~pow(2, 2) | ~(0b1111) & pow(2, 2))) # 0b1011
演算子の優先順位
** | べき乗 |
~, +, - | 単項の+, - |
*, /, //, % | 乗除 |
+, - | 二項間の+- |
>>, << | ビットシフト |
& | ビットand |
^, | | ビットxor, or |
<, >, <=, >= | 比較演算子 |
==, !=, <> | 等価演算子 |
=, +=など | 代入, 累算代入 |
is, not is | 同一オブジェクト比較 |
in, not in | 存在 |
and, not, or | 論理演算子 |
べき乗の注意
print(-1**2) # -1 print(2**-1) # 0.5 print(-2**-1) # -0.5
ビット計算の注意
10進数と2進数の計算は10進数で値が戻る。
print(type(0b11 + 1)) # <class 'int'> print(type(0b11 + True)) # <class 'int'>
こう見ると、python標準でのビット操作はめんどくさそう。
NumPyでdtype=boolでやってみると
import numpy as np a1 = np.ones(8, dtype=bool) a2 = np.array([1, 0, 1, 0, 1, 1, 0, 0], dtype=bool) print(a1) # [ True True True True True True True True] print(a2) # [ True False True False True True False False] print(a2.sum()) # 4 # ビットand print(a1 & a2) # [ True False True False True True False False] # ビットor print(a1 | a2) # [ True False True False True True False False] # ビットxor print(a1 ^ a2) # [False True False True False False True True] # ビットnot print(~a2) # [False True False True False False True True] # 特定のビット反転(右からn桁目) n = 2 a2[7-n] = not a2[7-n] print(a2) # [True False True False True False False False]
scikit-learn 最小二乗法
scikit-learnで最小二乗法
y=sin(x)に誤差をランダムで付加した100個のデータを準備し、
学習用:テスト用=8:2に分けて実行。
import numpy as np import matplotlib.pyplot as plt #sklearn.cross_validationは非推奨、次のヴァージョンで廃止予定 #sklearn.model_selectionへ移動 from sklearn.model_selection import train_test_split as tts from sklearn.linear_model import LinearRegression from sklearn.preprocessing import PolynomialFeatures from sklearn.pipeline import make_pipeline from sklearn.metrics import mean_squared_error as mse # y=sin(x)に誤差を付加したデータを作る data_size = 100 # サンプル数 x_orig = np.linspace(0, 1, data_size) def f(x): return np.sin(2 * np.pi * x) x = np.random.rand(data_size)[:, np.newaxis] y = f(x) + np.random.rand(data_size)[:, np.newaxis] - 0.5 x_train, x_test, y_train, y_test = tts(x, y, test_size=0.8) plt.plot(x_orig, f(x_orig), ":") plt.scatter(x_train, y_train) plt.xlim( (0, 1) )
# 最小二乗法の次数を変えたサブプロットを描画 row, column = 2, 3 # サブプロットの行数、列数 fig, axs = plt.subplots(row, column, figsize=(8, 6)) for deg, ax in enumerate(axs.ravel()): # パイプラインを作る e = make_pipeline(PolynomialFeatures(deg), LinearRegression()) # 学習 e.fit(x_train, y_train) # 予測 px = e.predict(x_orig[:, np.newaxis]) ax.scatter(x_train, y_train) ax.plot(x_orig, px, "m") ax.set(xlim=(0, 1), ylim=(-2, 2), yticks=([-1, 0, 1]), title="degree={}".format(deg)) plt.tight_layout() plt.show()
train_error = np.empty(10) test_error = np.empty(10) # 最適な次数を探す for deg in range(10): # 学習と予測 e = make_pipeline(PolynomialFeatures(deg), LinearRegression()) e.fit(x_train, y_train) # 誤差の評価 train_error[deg] = mse(y_train, e.predict(x_train)) test_error[deg] = mse(y_test, e.predict(x_test)) plt.plot(np.arange(10), train_error, "--", label="train") plt.plot(np.arange(10), test_error, label="test") # 最適次数を表示 plt.plot(test_error.argmin(), test_error.min(), "ro") plt.text(test_error.argmin()+0.5, test_error.min(), "best fit", color="r", backgroundcolor="w", fontsize=14) plt.ylim( (0, 1) ) plt.legend(fontsize=14) plt.show()
3次関数での近似が良さそう。
選んだ関数がsinなのでなんとなくうなづける。
今回のデータサイズだと、3~6次は大差なさそう。
乱数の初期値(seed)がかわると、4次や5次が最適となることもあった。
ただ、7次以上は学習用データで過学習をしてしまい、
テスト時の誤差が非常に大きくなることも。
遺伝的アルゴリズム
遺伝的アルゴリズムの例
①初期の遺伝子プール(個体群)を決める
②一定確率で遺伝子を交差させ、個体数を2倍にする
③一定確率で突然変異を起こす
④重みづけをして評価し、生き残る個体を選ぶ
⑤一定回数または目標値に達するまで②に戻る
16桁の2進数の数を正解として、遺伝的アルコリズムで正解をさがす例。
import matplotlib.pyplot as plt import numpy as np import random def mating(pool): """ランダムに選ばれる両親から交差し、個体数を2倍にする。 子のみのnewpoolを戻り値とする""" newpool = [] for i in range(len(pool)): p1, p2 = random.choice(pool), random.choice(pool) # 遺伝子が交差する地点を一か所選ぶ crosspoint = int(random.random()*16) div = pow(2, crosspoint) # 遺伝子を交差させて入れ替える f1 = (p1 // div)*div + p2 % div f2 = (p2 // div)*div + p1 % div newpool += [f1, f2] return newpool def mutation(pool): "突然変異。20%の個体に、1部位の突然変異が起きる" for i in range(len(pool)//5): p_index = int(random.random()*len(pool)) p1 = pool[p_index] mutate_point = int(random.random()*16) bit = pow(2, mutate_point) # bitを1つだけ反転させる p_bit = p1 & bit # p1 = p1 - p_bit + (bit - p_bit) p1 = p1 + bit - p_bit * 2 pool[p_index] = p1 return def selection(pool) ->list: "2倍に増えた個体数をもとに自然淘汰する。上位から40%、下位から10%を残す" evaluate = [] for i in pool: e = 16 - bin((i^d)).count("1") evaluate.append( (i, e) ) evaluate.sort(key=lambda x:x[1], reverse=True) # 上位と下位に分ける l = len(evaluate) upper, lower = evaluate[:l//2], evaluate[l//2:] # random.shuffleはin placeで戻り値はNone random.shuffle(upper) random.shuffle(lower) survived = upper[:int(l*0.4)] + lower[:data_size - int(l*0.4)] return [i for i, e in survived] def random_selection(pool): "自然淘汰をせずにランダムで個体を残す" random.shuffle(pool) return pool[:data_size] def print_evaluate(pool, generation, flag): """合致するビット数を評価 flat=True のときは、各個体の評価を表示. flag=False のときは、世代数と平均のみ表示""" evaluate = [] for i in pool: evaluate.append(16 - bin((i^d)).count("1")) if flag: for i, e in zip(pool, evaluate): print(bin(i)[2:].zfill(16),":", e) print("第{}世代".format(generation), end=" ") score = sum(evaluate)/len(evaluate) print("平均:", score, "\n"*flag) return score def main(pool, select=True, print_flag=True): "世代を重ねるメインループ" generation = 0 score = [] score.append(print_evaluate(pool, generation, print_flag)) for i in range(40): pool = mating(pool) mutation(pool) if select: pool = selection(pool) else: pool = random_selection(pool) generation += 1 score.append(print_evaluate(pool, generation, print_flag)) return score if __name__ == "__main__": d = 0b0101011000001111 data_size = 20 # initialize #pool = [int(random.random()*256) for i in range(data_size)] pool = np.random.randint(65536, size=data_size, dtype=int) pool2 = pool.copy() scores = main(pool, select=True, print_flag=False) scores2 = main(pool2, select=False, print_flag=False) x = np.arange(len(scores)) plt.plot(x, np.array(scores)/16, "-", label="selection") plt.plot(x, np.array(scores2)/16, ":", label= "without selection") plt.legend(fontsize=14) plt.ylim(0, 1)
print_flag=Trueとすると、各世代の個体を表示。
1001100010111001 : 6 1000000011110001 : 4 1001000111110001 : 4 1000100010001001 : 7 1000000011110001 : 4 1001100011110001 : 4 1001000111110001 : 4 1001000111110001 : 4 1000000010111010 : 6 1001000011010001 : 6 1001000111110001 : 4 1001100010111010 : 6 1001000010111011 : 8 1001000111110001 : 4 1001000110110001 : 5 1001100010111011 : 7 1000000010101001 : 7 1001000110110000 : 4 1001000011111010 : 6 0011000010110001 : 6 第40世代 平均: 5.3
print_flag=Falseとすると、各世代の平均のみ表示
第0世代 平均: 8.4 第1世代 平均: 9.25 第2世代 平均: 10.85 第3世代 平均: 10.95 第4世代 平均: 11.7 第5世代 平均: 12.2 ・・・ 第37世代 平均: 15.9 第38世代 平均: 15.95 第39世代 平均: 16.0 第40世代 平均: 15.8
今回の例だと20世代ほどでほぼ正解に辿りついている。
課題:
numpyの2進数の扱い方がよくわからず、あまり使えていない。
それ以前にpythonで2進数をどう扱うか勉強が必要。
特定のbitだけ演算をする方法を調べ中。
bit演算の優先順位なども頭に入れないと。
自然選択なしで、多数の試行を重ねて結果をヒストグラムなりにしても面白そう。
NumPyのboolで
無理にビット演算をするのをやめて、NumPyのdtype=boolで書き直してみた。
import matplotlib.pyplot as plt import numpy as np import random def mating(pool): """ランダムに選ばれる両親から交差し、個体数を2倍にする。 子のみのnewpoolを戻り値とする""" newpool = [] for i in range(data_size): # ランダムで親を選ぶ p1, p2 = random.choice(pool), random.choice(pool) # 遺伝子が交差する地点を一か所選ぶ crosspoint = int(random.random()*16) f1 = np.hstack((p1[:crosspoint], p2[crosspoint:])) f2 = np.hstack((p2[:crosspoint], p1[crosspoint:])) # 遺伝子を交差させて入れ替える newpool += [f1, f2] return np.array(newpool) def mutation(pool): "突然変異。20%の個体に、1部位の突然変異が起きる" for i in range(len(pool)//5): p_index = int(random.random()*len(pool)) p1 = pool[p_index] mutate_point = int(random.random()*16) p1[mutate_point] = ~p1[mutate_point] pool[p_index] = p1 return def selection(pool): "2倍に増えた個体数をもとに自然淘汰する。上位から40%、下位から10%を残す" # 正解数で降順にソート pool = sorted(pool, key=lambda x:(x==d).sum(), reverse=True) # 上位と下位に分ける l = len(pool) upper, lower = pool[:l//2], pool[l//2:] # random.shuffleはin placeで戻り値はNone random.shuffle(upper) random.shuffle(lower) # 上位から80%,下位から20%選ぶ survived = np.vstack((upper[:int(l*0.4)], lower[:data_size - int(l*0.4)])) return survived def random_selection(pool): "2倍に増えた個体数から自然淘汰をせずにランダムで個体を残す" random.shuffle(pool) return pool[:data_size] def print_evaluate(pool, generation, flag): """合致する数を評価 flat=True のときは、各個体ごとの評価を表示. flag=False のときは、世代数と平均のみ表示""" # 正解数をカウント evaluate = (pool==d).sum(axis=1) # 表示フラグがTrueなら各個体を表示 if flag: for i, e in zip(pool, evaluate): print(i, ":", e) # flagに関係なく、各世代の平均を表示 print("第{}世代".format(generation), end=" ") score = sum(evaluate)/(data_size * 16) print("平均:", score, "\n"*flag) return score def main(pool, select=True, print_flag=True): "世代を重ねるメインループ" generation = 0 score = [] score.append(print_evaluate(pool, generation, print_flag)) for i in range(20): pool = mating(pool) mutation(pool) if select: # 自然淘汰ありのとき pool = selection(pool) else: # 自然淘汰なしのとき pool = random_selection(pool) generation += 1 score.append(print_evaluate(pool, generation, print_flag)) return score if __name__ == "__main__": # initialize d = np.array([0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1], dtype=bool) data_size = 20 pool = np.random.randint(0, 2, (data_size, 16), dtype=bool) pool2 = pool.copy() # 自然淘汰あり scores = main(pool, select=True, print_flag=True) # 自然淘汰なし scores2 = main(pool2, select=False, print_flag=False) # グラフ表示 x = np.arange(len(scores)) plt.plot(x, np.array(scores), "-", label="selection") plt.plot(x, np.array(scores2), ":", label= "without selection") plt.legend(fontsize=14) plt.ylim(0, 1) plt.title("Genetic Argorithm", fontsize=14) plt.show()
matplotlib グラフの設定
matplotlibのグラフの設定について
日本語の表示
日本語を表示する場合は、表示するごとにfontproperties = fpをセットする。
from matplotlib.font_manager import FontProperties fp = FontProperties(fname='C:\WINDOWS\Fonts\msgothic.ttc', size=14) plt.title("折れ線グラフ", fontproperties=fp)
グラフのタイトル
plt.title(str, loc='center')
・表示位置
loc='center' | 中央。デフォルト |
loc='left' | 左寄せ |
loc='right' | 右寄せ |
表示範囲(変域)
・両軸の表示範囲の取得
xmin, xmax, ymin, ymax = plt.axis()
・両軸の表示範囲の設定
plt.axis(xmin, xmax, ymin, ymax)
・軸ごとに範囲を設定
plt.xlim(xmin, xmax) plt.ylim(ymin, ymax) plt.xlim(xmax=10) # xminは変えずに、xmaxだけ変更 plt.xlim(xmin=0) # xmaxは変えずに、ximnだけ変更
軸
ax = plt.gca() # get current axis
ax.xaxis.set_ticks_position("bottom") # 目盛りは軸の下
・枠と目盛りを消去
plt.axis("off")
目盛り
・現在の横軸の位置配列、ラベル配列の取得
xlocs, xlabels = plt.xticks()
・現在の縦軸の位置配列、ラベル配列の取得
ylocs, ylabels = plt.yticks()
・目盛りの配分
plt.xticks(-4, 4, 9) # -4から4まで9個(8等分) plt.sticks([-1, 0, 1]) # -1, 0, 1の3つ
・目盛りの配分とラベルを同時に設定
plt.xticks(xlocs, xlabels, color, rotation) # xlocs:位置の配列 xlabels:ラベルの配列
グラフの枠線
・枠線を消す
ax = plt.gca() # get current axis ax.spines["right"].set_color("none") # 右消し ax.spines["left"].set_color("none") # 左消し ax.spines["top"].set_color("none") # 上消し ax.spines["bottom"].set_color("none") # 下消し
・枠線の色を変える
上記でset_color("blue")など
凡例
plt.plot(x, y, color=~, label="y=sin(x)") # 凡例の設定 plt.legend(loc="upper_left") # 凡例の表示
グリッドを表示する
plt.grid(color='gray')
水平線、垂直線、直線
・水平線をまとめて引く
plt.hlines(seq, xmin, xmax, colors="k", linestyle="solid", label="")
・垂直線をまとめて引く
plt.vlines(seq, ymin, ymax, colors="k", linestyle="solid", label="")
・直線を1本引く
plt.plot({x1, x2], [y1, y2])
・折れ線を引く
plt.plot(seqx, seqy)
・グラフの大きさ
inchで指定
plt.figure(figsize=(width,height), dpi=80)
・サブプロットに分ける
# グラフエリアを縦2、横3に分割してその1番目 plt.subplot(2, 3, 1) plt.plot(x, y1) # そのサブエリアに描画 # グラフエリアを縦2、横3に分割してfig配列、ax配列に格納 fig, ax = plt.subplots(2, 3) ax[0, 0].plot(x, y) # その0行0列目のサブエリアにプロット # サブエリアの大きさの比率を変える from matplotlib import gridspec gs = gridspec.GridSpec(2, 2, width_ratios=(6, 1), height_ratios=(4, 1)) plt.subplot(gs[0, 0]).plot(x, y) # 0行0列目にプロット
・グラフの塗りつぶし
plt.fill(x, y, color)
キーワードいろいろ
・線種linestyle, ls
solid | - | 実線 |
dashed | -- | 破線 |
dashdot | -. | 破点線 |
dotted | : | 点線 |
(offset, on-off-dash-seq) |
・線の幅 linewidth, lw
floatで単位はpoint
サンプル
サンプル1
import matplotlib.pyplot as plt import numpy as np from matplotlib.font_manager import FontProperties fp = FontProperties(fname='C:\WINDOWS\Fonts\msgothic.ttc', size=14) x = np.linspace(-np.pi, np.pi*1.1, 100) plt.plot(x, np.cos(x), "r", label="y=cos(x)") plt.plot(x, np.sin(x), "olive", label="y=sin(x)") plt.title("三角関数", loc="center", fontproperties=fp) plt.xlim(xmin=-np.pi) plt.ylim(-1.1, 1.1) xtick = np.array(["-π", "-1/2π", "0", "1/2π", "π"]) locs = np.linspace(-np.pi, np.pi, 5) plt.xticks(locs, xtick, color="c", fontsize=14, rotation=30) plt.yticks([-1, 0, 1]) ax = plt.gca() # get current axis ax.spines["right"].set_color("none") # 右枠消し ax.spines["top"].set_color("none") # 上枠消し ax.spines["left"].set_color("m") # 左枠をマゼンダに ax.spines["bottom"].set_color("c") # 下枠をシアンに plt.vlines(np.linspace(-np.pi, np.pi, 5), -1.1, 1.1, "c",linestyle=":", lw=1) plt.hlines([0], -np.pi, np.pi*1.1, "m", linestyle=":", lw=1) plt.legend() # 凡例の表示 plt.show()
サンプル2
import matplotlib.pyplot as plt import numpy as np from matplotlib.font_manager import FontProperties fp = FontProperties(fname='C:\WINDOWS\Fonts\msgothic.ttc', size=14) from matplotlib import gridspec x = np.random.randn(1000) y = np.random.randn(1000) gs = gridspec.GridSpec(2, 2, width_ratios=(6, 1), height_ratios=(4, 1)) ax = [plt.subplot(gs[0, 0]), plt.subplot(gs[0, 1]), plt.subplot(gs[1, 0])] # メインの散布図 ax[0].set_title("散布図", fontproperties=fp) ax[0].scatter(x, y, c="c", alpha=0.5) # 不透明度0.5 ax[0].grid() # グリッド表示 # 右側のヒストグラム ax[1].hist(y, 50, orientation="horizontal") ax[1].invert_xaxis() # 左右反転 ax[1].yaxis.tick_right() # 縦軸の目盛りを右側に配置 # 下のヒストグラム ax[2].hist(x, 100) ax[2].tick_params(labelbottom="off") plt.tight_layout() plt.show()
サンプル3
import matplotlib.pyplot as plt import numpy as np # 3x3の9個のサブプロットに分割 fig, axs = plt.subplots(3, 3, figsize=(8, 6)) t = np.linspace(0, np.pi*2, 100) for i in range(3): for j in range(3): axs[i, j].plot(np.cos(t*(i+1)), np.sin(t*(j+1)))
サンプル4
import matplotlib.pyplot as plt import numpy as np x = np.linspace(-2, 2, 100) # 2x3の6個のサブプロットに分割 for i in range(5): plt.subplot(2, 3, i+1) plt.plot(x, x**i) plt.title("y=x^{}".format(i)) plt.tight_layout() plt.show()
例外処理
pythonの例外処理について。
例外が発生すると例外オブジェクトを作る。
すべての例外オブジェクトはExceptionクラスを継承している。
基本形は
try: 処理 except: 例外発生時の処理
例外処理の書式
except | すべての例外を捕捉 |
except 例外クラス名 | 捕捉する例外を指定 |
except 例外クラス名 as ~ | 捕捉する例外を指定し、変数に代入 |
else | 例外が発生しなかった時の処理 |
finally | 例外の有無にかかわらず行う処理 |
例外のクラス指定はサブクラスまで含めて判定
・エラーが捕捉されなかった場合は、トレースバックを表示。
・例外処理のメリット
エラーの発生位置とエラー処理を分離できる。
・tryブロックはできるだけ短くする。
長いコードを記述すると、意図せぬ部分からの例外をとらえてしまう。
・tryブロックから呼び出された関数の例外もとらえる。
raise
・自作のクラスなどでは、意図的に例外を発生させることができる。
# dictクラスを継承し、strだけキーにできるようにする class StrDict(dict): def __setitem__(self, key, value): if not isinstance(key, str): raise ValueError("Key must be str.") super().__setitem__(key, value) d1 = StrDict() #d1[0] = 0 # ValueError: Key must be str.
既存の例外クラスを継承して、独自の例外クラスを作れる。
tracebackモジュール
例外発生時のトレースバックを操作でき、例外が発生しても処理を続けられる。
import traceback d1 = {1:"Jan", 2:"Feb", 5:"may"} err = [] for i in range(6): try: print(d1[i]) except: err.append(traceback.format_exc()) # 例外を文字列として保持 print(len(err)) #print(err) # エラー内容を確認するとき表示 # Jan # Feb # may # 3
traceback.print_exc()で例外を表示できる。
モジュール
1つのモジュールは~.pyという1つのスクリプトファイルに相当する。
__name__
スクリプトファイルは単独のプログラムとしても動作するし、他のプログラムやモジュールから呼び出して使うこともできる。
・__name__属性
単独のプログラムとして起動したときは"__main__"が代入される。
モジュールとして呼ばれたときはファイル名の.pyの前が代入される。
if __name__ == "__main__":
とすると、単独プログラムで実行された時の動作を記述できる。
importの挙動
import math a = 1 def func(): return 100 class Test(int): pass print("__name__は", __name__) # __name__は __main__ if __name__ == "__main__": print("testmodule.pyを起動しました") else: print("testmodule.pyをimportしました")
これをtestmodule.pyとして保存し、他のプログラムからimportしてみると
import testmodule as tm # __name__は testmodule # testmodule.pyをimportしました a = 2 # モジュール変数,関数,クラスはモジュール名をつける print(tm.a, a) # 1 2 print(tm.func()) # 100 c = tm.Test("3") #importしたモジュールでimportしたモジュールが使える d = tm.math.sqrt(400) print(c, d) # 3 20.0
複数モジュールのimportはもちろん、多重importもできるが、
関数名、クラス名が同一のものがある場合は、あとからimportしたモジュールで上書きされる。
from module import * のようにすべての関数をimportするときは注意。
importの書き方
import module | シンプルな書き方 |
import module as mo | moduleをimportし、moという名で扱う |
import m1, m2, ... | カンマ区切りでmoduleを列挙できる |
from module import f1, f2, ... | カンマ区切りで関数,クラスを列挙できる |
from module import * | すべての関数をimportし、module名なしで使う |
from module import func | func関数だけをimportし、module名なしで使う |
パッケージ
スクリプトファイルをフォルダでまとめると、複数のモジュールをひとまとまりのパッケージとして扱える。
パッケージとして使うフォルダには__init__.pyという名前のファイルを設置する。
__init__.pyファイルは空ファイルでもいいし、初期化用のコードを書いてもよい。
階層は何層にも重ねることができる。
package1フォルダのmodule1ファイルをimportするときは、
import package1.module1 などとする。