蛇使いな彼女BLOG
【第99回】クラスオブジェクトの条件分岐とmatch caseに関して①
2024.02.16
モトハシです! 今日は前から気になっていたmatch文を使った条件分岐を試してみたいと思います。
match自体はPython3.0から実装されているらしいのですが、普段使用している3.6だとif文しか使えなかったので、今回はこのように新しく仮想環境を構築して作業を進めていきます。
ちなみに現在最新のバージョンはpytho3.12.0(2023.10リリース)なので、モトハシが使っているバージョンはかなり古いです。なぜアップグレードしないかというと、pandasやnampyなど他のライブラリと互換性が無くなるとこれまでのプログラムが動かなくなったり、事実上壊れるからです(e_e)
※環境に関しての補足※ base(Anaconda5.0.0)はPythonのメイン環境で、仮想環境は 「\Anaconda3\envs\ 」直下にある3つ全てが当てはまります。py310という名前の環境がPython3.10搭載の環境です。 matplotlib もバージョンによって仕様が頻繁に変わるので、以下のように環境を作り動作テストの為に利用しています。
(base) C:\Users\User\Documents>conda info -e
# conda environments:
#
base * C:\Users\HYDROLAB\Anaconda3
matplot_test C:\Users\HYDROLAB\Anaconda3\envs\matplot_test
matplotlib310 C:\Users\HYDROLAB\Anaconda3\envs\matplotlib310
py310 C:\Users\HYDROLAB\Anaconda3\envs\py310
まず以下のスクリプトを見てください。これはRAWデータの処理と実際にあった不具合を模して作成したコードです。生成される任意配列はランダムな値で、各列の最初の値が整数の0~9に当てはまるデータを抜き出して図化するプログラムです
# Python3.6 by Anaconda5.0.0 import numpy as np import matplotlib.pyplot as plt # 任意データ生成 r=np.random.default_rng() data=r.uniform(0,200,(25,100)) class Data_selection(): def __init__(self,d=data,): self.d=d def search(self,num=0): #1行目を整数に丸めてnumと一致する列を返す col=np.where(np.round(data[0,:],0)==num)[0] res=data[:,col] return res Ds=Data_selection() for i in range(10): item=Ds.search(num=i) fig,ax = plt.subplots(figsize=(3, 3)) ax.plot(item) fig.savefig('fig/Number_{}.png'.format(i)) plt.close()
プログラムを実行すると、指定された整数から始まる列が無い場合このようなエラーが発生します。
出力結果
...
Traceback (most recent call last): File "<stdin>", line 6, in <module> File "C:\Users\HYDROLAB\Anaconda3\lib\site-packages\matplotlib\__init__.py", line 1710, in inner return func(ax, *args, **kwargs) File "C:\Users\HYDROLAB\Anaconda3\lib\site-packages\matplotlib\axes\_axes.py", line 1437, in plot for line in self._get_lines(*args, **kwargs): File "C:\Users\HYDROLAB\Anaconda3\lib\site-packages\matplotlib\axes\_base.py", line 404, in _grab_next_args for seg in self._plot_args(this, kwargs): File "C:\Users\HYDROLAB\Anaconda3\lib\site-packages\matplotlib\axes\_base.py", line 394, in _plot_args seg = func(x[:, j % ncx], y[:, j % ncy], kw, kwargs) ZeroDivisionError: integer division or modulo by zero >>>
この不具合を修正するためには、繰り返し構文の中で条件分岐を行うのが一般的ですが、クラスオブジェクト構造だと想定外の結果を返す事が分かりました。
for i in range(10): item=Ds.search(num=i) if len(item)==0: break fig,ax = plt.subplots(figsize=(3, 3)) ax.plot(item) fig.savefig('fig/Number_{}.png'.format(i)) plt.close()
出力結果 ZeroDivisionError: integer division or modulo by zero >>> item.shape (25, 0) >>> print(item) [] >>> len(item) 25 >>>
何が起こっているかと言うと、search()が実行された時、指定された列があっても無くても25行という構造を保ったまま空の配列が排出されているというような状況です。このような場合、一般的にはtry~exceptの文章でエラー回避を行うのが普通ですが、インデント箇所が増えて見づらくなるデメリットがあります。特に大きなプログラムでは付随するメソッドが増える為、後から見たときに何をしているか分かりやすい方がいいですよね。
そこで次回はPython3.10から新たに導入されたmatch caseを使って条件分岐を行ってみようと思います。