【自前python講座】ユーザー定義関数/クラス/メソッド/モジュール

python-def-class プログラミング
python-def-class



自前の python 講座用資料です.
今回は,プログラミングにて処理を使いまわす際に有益な,ユーザー定義関数/クラス/メソッド/モジュールについて紹介します.
一口に「関数」というと,組み込み関数とか標準関数も関数なのですが,ユーザー定義関数とは,言語仕様に無い例えば自分で定義した関数のことで,クラス/メソッド/モジュールについても同様です.
これらが使えると,何度も同じ処理を書かずに使いまわすことができたり,きちんと処理を表す名前を定義しておけばコードが読みやすくなったり,他のプログラムから持ってきたりすることができ,生産性向上につながります.
また,ページの最後に演習を用意いたしましたので,ぜひチャレンジしてみてください.

今回のコードについては,以下の github にも記載しております.

https://github.com/KazutoMakino/PythonCourse/blob/main/006_def_class/006_def_class.ipynb

それぞれの違いについて

関数やクラスやメソッドやモジュール,あとはサブルーチンやパッケージなんてものもありますが,初学者にとっては,それぞれ,どれがどれなのか混同しやすいので,初めにざっくりと概要を説明いたします.

  • 関数・・・・関数は英語で function ですが,function の別の和訳として ”機能” であるように,値を入力すると,これに対するひとまとまりの処理が実行された出力を返す機能/動作です.入力を引数(ひきすう, parameter),出力を戻り値(return value)と呼びます.これらは処理に応じてそれぞれある場合とない場合の両方が存在します.
  • クラス・・・・データや処理を入れるための入れ物で,この中身のデータ/処理は,クラスを通して取り出したり使うことができます.設計図と呼ばれたりすることもあります.
  • メソッド・・・・クラスの中にある関数です.
  • モジュール・・・・上記の関数/クラス/メソッドのスクリプトが含まれているファイル自体を指します.
  • サブルーチン・・・・関数と同義という説が多いですが,例えば fortran では function は戻り値があり,subroutine は戻り値がないという違いがあります.戻り値がないため,subroutine には型は定義されません(C だと実質 void の関数で,Python だと戻り値がなし/None の関数).Python においては言語仕様上で関数と区別する必要がなく,サブルーチンという単語が出てくることはほとんどないでしょう.
  • パッケージ・・・・モジュールが色々入ったもの,つまりフォルダを表します.

大学のサークルの数学の先生は,「関数は変数を入れて(函れて)出力させるためのものだから “函数” と書くほうが適切」と仰っていました.

関数定義

関数の定義の仕方は,簡単には以下のように書くことができます.

def 関数名(仮引数_1, 仮引数_2, ..., 仮引数_n):
    処理
    return 戻り値

関数の定義には def を用います.
(可読性やエディタの linter 性能を向上させる型アノテーションや Docstring については,またの機会とします)
ユーザー定義関数の引数について,この段階では具体的な値はまだ決まってないので,接頭辞として”仮”がついて仮引数と呼ばれます.逆に,定義済みの例えば組み込み関数の print だと,print(val) の val は実引数と呼びます.
C言語とかのように,関数プロトタイプ宣言や型をつけなくても良いので,シンプルで楽ですね.
関数は動作なので,関数名にはできるだけ動詞のみ,もしくは,動詞と目的語のペアとしましょう.
また,python において関数名は snake case でつけましょう(後述).
例えば,RMS (Root Mean Square, 二乗平均平方根) を返す関数だと,calc_rms みたいな感じです.

変数や関数を命名するに当たり,可読性を上げるために一定の規則(命名規則)が存在します.
先ほど snake case という用語を記載しましたが,これは PEP8 で定められた命名規則における大文字/小文字/アンダーバーを駆使した名前の付け方の一種であり,以下が挙げられます.

名前(和)名前(英)書き方例別名python で使う名前
スネークケースsnake casesnake_caselower snake case変数名,関数名,メソッド名,インスタンス名
キャメルケースcamel casecamelCase使わない
パスカルケースPascal casePascalCaseupper camel caseクラス名,例外,型変数
コンスタントケースconstant caseCONSTANT_CASEupper snake case定数
Cases

ちなみに,モジュール名とパッケージ名は全て小文字でなるべく短くして,アンダーバーは非推奨とされています.
また,その関数やクラスでしか用いない変数やメソッドの名前については,最初にアンダーバーをつけることが推奨されています.
処理によっては,引数も戻り値も不要な関数もあり得るので,その場合は以下のように記載します.

def 関数名():
    処理

「戻り値はない」と言いましたが,実際には return None が省略されていて None が返されます.

関数定義において,仮引数それぞれに,以下のように 仮引数=初期値 と書くことで初期値を与えることができます.

def 関数名(仮引数_1, 仮引数_2=初期値, ..., 仮引数_n=初期値):
    処理
    return 戻り値

全てにおいて初期値を設定しなくても良いので,上の例では 仮引数_1 には初期値を与えていませんが,後述する関数呼び出し時の位置引数の関係上,初期値のない仮引数は左側にまとめておく必要があります.

上記で定義した関数の呼び出しは以下のように書きます.

関数名(実引数_1, 実引数_2, ..., 実引数_n)

このとき,定義した関数の仮引数と呼び出す関数で与える実引数は,順番と個数があっている必要があります.
このような書き方をする引数を位置引数と呼びます.
関数呼び出しには別の書き方があり,

関数名(仮引数_1=実引数_1, 仮引数_2=実引数_2, ..., 仮引数_n=実引数_n)

という風に,仮引数=実引数 としても呼び出すことができ,この場合,仮引数名で参照できるため順番は守らなくても呼び出すことができます.
関数定義で,仮引数に初期値が決まっているものについては,以下のように,呼び出し時に実引数を与えなくても,初期値があるので問題なく動作します.

def 関数名(仮引数_1, 仮引数_2=初期値):
    処理
    return 戻り値

関数名(実引数_1)

しかし,上記例で,仮引数_1に初期値があり,仮引数_2に初期値がない状態で定義したとして,関数名(実引数_2) として呼び出すと,位置引数の実引数_2は果たしてどちらの実引数なのか不明になるためエラーになります.
これが,関数定義で初期値のない仮引数は最初に書く必要がある理由です.

前置きが長くなってしまいましたが,a と b を足すだけという関数について,以下に例を示します.

def add_a_b(a, b):
    return a + b
​
​
add_a_b(3, 4)
# 7
def add_a_b(a, b):
    return a + b
​
​
add_a_b(b=4, a=3)
# 7
def add_a_b(a=1, b=20):
    return a + b
​
​
add_a_b(b=4, a=3)
# 7
def add_a_b(a=1, b=20):
    return a + b
​
​
add_a_b()
# 21
def add_a_b(a=1, b=20):
    return a + b
​
​
add_a_b(b=3)
# 4
def add_a_b(a=1, b=20):
    return a + b
​
​
add_a_b(3)
# 23
def add_a_b(a=1, b):
    return a + b
​
​
add_a_b(b=3)
#   File "C:\Users\___\AppData\Local\Temp/ipykernel_10608/227892080.py", line 1
#     def add_a_b(a=1, b):
#                 ^
# SyntaxError: non-default argument follows default argument

この例は,関数定義の時にエラーが出ており,一つの位置引数を用いて呼び出す場合を考慮すると,a,b どちらの実引数とすれば良いか分からなくなるためエラーが出ています.

def add_a_b(a, b):
    print(a + b)
​
add_a_b(a=5, b=5)
# 10
type(add_a_b(a=5, b=5))
# 10
# NoneType

クラス定義/メソッド定義

クラス及びメソッドの定義の仕方は,簡単には以下のように書くことができます.

class クラス名:
    処理

    def __init__(self, 仮引数_1, 仮引数_2, ..., 仮引数_n):
        処理

    def メソッド_1(self, 仮引数_1, 仮引数_2, ..., 仮引数_n):
        処理

    def メソッド_2(self, 仮引数_1, 仮引数_2, ..., 仮引数_n):
        処理

    ...

    def メソッド_n(self, 仮引数_1, 仮引数_2, ..., 仮引数_n):
        処理

(クラス継承については飛ばします)
クラスの定義には class を用います.
クラス内(インデントブロック)において def によって関数が定義されていますが,クラス内の関数はメソッドと呼ばれ,class に紐づいた関数です.
クラス内の変数はメンバー変数と呼ばれ,このクラス内でのみ有効で,メソッド内で用いる時は慣例上 self という名前の変数に格納し,self.変数名 という風に参照します.
クラスの中でメソッドの外にある処理は,クラスが呼び出された時に実行され,この処理の代入式によって定義された変数について,メソッド内でこれを参照する場合も,self.変数名 という風に参照することができます.使うシーンとしては,例えば,このクラス内で使いまわしたい定数などが挙げられます.
実際のところ,self という名前でなくてもプログラムは動くのですが,python という言語において広く浸透している一般的なルールなので,self という名前にしましょう.
さて,保留にしていた __init__ ですが,これはコンストラクタと呼ばれ,クラスを呼び出したときに自動的に 1 度だけ実行される処理の塊です(メソッドと言ってよいものかは不明).例えば,クラスを呼び出すときに,クラスの外から変数を入力するときに用います.
<クラスの呼び出し>と先ほどから書いていますが,これをインスタンス化と呼び,インスタンス化されたオブジェクトをインスタンスと呼びます.
プログラミングをあまり触ったことない方にとっては,分からない単語のトリプルアクセル状態なので,次に,上記 <クラス名> クラスに対するインスタンス化の構文を示します.

インスタンス = クラス名(仮引数_1=実引数_1, 仮引数_2=実引数_2, ..., 仮引数_n=実引数_n)

インスタンス化(クラス呼び出し)は,上記のように関数呼び出しと同じように定義でき,引数の考え方も同じです.
引数は __init__ にて処理されます.
上記を実行すると,<クラス名>クラスが呼び出され,クラス内の直下の処理と,__init__ が実行されて,この属性が<インスタンス>に代入されます.
個人的には,「インスタンスは,クラスという正式名称に対してあだ名をつけたもので,クラスの入力に対する属性(メンバー変数やメソッド)が含まれる」というイメージです.
インスタンスを用いて,クラス外からメンバー変数を参照する場合は,次のように記載します.

インスタンス.メンバー変数

self が インスタンス になっただけですね.
メソッドを使いたい場合も同様です.

インスタンス.メソッド名(仮引数_1=実引数_1, 仮引数_2=実引数_2, ..., 仮引数_n=実引数_n)

クラスは,メンバー変数やメソッドの入れ物であるので,クラスおよびインスタンスの名前の付け方としては,どちらも名詞が好ましく,pascal case で名付けます.
メソッドはクラス内の関数ですので,関数と同様に動詞と目的語で名付けます.
このように名付けると,<インスタンス.メソッド>というスクリプトが,<名詞.動詞_目的語>のような見た目になり,まるで英文を書いているかのような,可読性の高いコードを構築することができます(何より気持ち良い).

上記のような概念や理屈は,1 回自分で書いたことのある人でないと理解が厳しいので,以下に示した例について雰囲気をつかんでいただき,もう一回上記説明をご覧いただくと,より深くご理解いただけると思います.

class MyClass:
    a = 1
​
    def __init__(self, b=2):
        self.b = b
​
    def shows_member(self):
        print(f"self.a={self.a}, self.b={self.b}")
​
​
myclass = MyClass(b=10)
myclass.shows_member()
# self.a=1, self.b=10

ここでインスタンスを用いて,これのメンバー変数を呼び出してみると,

myclass.b
# 10

というようにメンバー変数を参照することができます.
上記メンバー変数はインスタンスである myclass の中でのみ定義されているので,myclass. を消した変数 a では,「変数が未定義」のエラーが送出されます.

a

# ---------------------------------------------------------------------------
# NameError                                 Traceback (most recent call last)
# ~\AppData\Local\Temp/ipykernel_10608/2167009006.py in <module>
# ----> 1 a
# 
# NameError: name 'a' is not defined
class MyClass:
    def __init__(self, a, b=2):
        self.a = a
        self.b = b
​
    def shows_member(self, c):
        print(f"self.a={self.a}, self.b={self.b}, c={c}")
​
​
myclass = MyClass(a=55)
myclass.shows_member(c=100)
# self.a=55, self.b=2, c=100
class MyClass:
    a = 100
    b = 200
    c = 300
​
    def shows_member(self):
        print(f"self.a={self.a}, self.b={self.b}, self.c={self.c}")
​
​
myclass = MyClass()
myclass.shows_member()
# self.a=100, self.b=200, self.c=300
class MyCalculator:
    z = 100
    
    def __init__(self, x, y=10):
        self.x = x
        self.y = y
        
    def add_xyz(self):
        return self.x + self.y + self.z
    
    def get_z_squared(self):
        return self.z**2
    
    def returns_value_sqrt(self, value):
        return value**(1/2)


mycalc = MyCalculator(x=1)
mycalc.add_xyz()
# 111
mycalc.get_z_squared()
# 10000
mycalc.returns_value_sqrt(value=2)
# 1.4142135623730951

上記で定義したクラス: MyCalculator の 3 つのメソッドを見てどう思いますでしょうか?
add_xyz は,MyCalculator 直下の self.z と,インスタンス化のときに代入する self.x, self.y を用いているので,クラスとして扱う恩恵は大いにありそうです.
一方,get_z_squared は,self.z しか使っていないので,インスタンス化のときに代入する値を用いないので,インスタンス化の必要性が感じられません.
get_z_squared のように,クラス直下のメンバー変数のみしか用いることのないメソッドの呼び出しについて,

class クラス名:
    処理

    @classmethod
    def クラスメソッド名(cls, 仮引数_1, 仮引数_2, ..., 仮引数_n):
        cls に格納されている,クラス直下のメンバー変数のみの処理

という風に,def の前に @classmethod というコードを付け足すことにより,そのメソッドはクラスメソッドと呼ばれる属性になります.
また,メンバー変数は self に格納していましたが,クラスメソッドであることを明示的に区別するために,慣例的にクラスメソッド内では cls を用います.
クラスメソッドは次のように簡単に呼べます.

クラス名.クラスメソッド(仮引数_1=実引数_1, 仮引数_2=実引数_2, ..., 仮引数_n=実引数_n)

ということで,インスタンス化の必要がなくなり,__init__ に渡す引数がなくなりました.
先ほどの MyCalculator クラスの get_z_squared をクラスメソッドにして呼び出した場合を以下に示します.

class MyCalculator:
    z = 100
    
    def __init__(self, x, y=10):
        self.x = x
        self.y = y
        
    def add_xyz(self):
        return self.x + self.y + self.z
    
    @classmethod
    def get_z_squared(cls):
        return cls.z**2
    
    def returns_value_sqrt(self, value):
        return value**(1/2)


MyCalculator.get_z_squared()
# 10000

さらに,returns_value_sqrt は引数の value しか用いないので,クラスである必要性が感じられず,関数で良いように思えます.
もちろん,関数にしても良いのですが,このメソッドがクラスと関連があることを明示したい場合に,次のように書くことで,あたかも「クラスと関連付けた関数」としてメソッドを呼び出すことができます.

class クラス名:
    処理

    @staticmethod
    def クラスメソッド名(仮引数_1, 仮引数_2, ..., 仮引数_n):
        メンバー変数は用いない処理

という風に,def の前に @staticmethod というコードを付け足すことにより,そのメソッドは静的メソッドと呼ばれる属性になります.
静的メソッドではメンバー変数を用いないので,引数に self が無いことに注意してください.
静的メソッドは次のように呼べます.

クラス名.静的メソッド(仮引数_1=実引数_1, 仮引数_2=実引数_2, ..., 仮引数_n=実引数_n)

先ほどの MyCalculator クラスの returns_value_sqrt を静的メソッドにして呼び出した場合を以下に示します.

class MyCalculator:
    z = 100
    
    def __init__(self, x, y=10):
        self.x = x
        self.y = y
        
    def add_xyz(self):
        return self.x + self.y + self.z
    
    @classmethod
    def get_z_squared(cls):
        return cls.z**2
    
    @staticmethod
    def returns_value_sqrt(value):
        return value**(1/2)

MyCalculator.returns_value_sqrt(value=2)
# 1.4142135623730951

モジュール定義

モジュールは,今までに挙げた関数やクラスが書いてあるスクリプトのファイルです.
コードが長くなってくると,保守性が悪くなったり,エディタのコードオートフォーマットが重くなったりするので,こういう場合はファイルを分けます.
また,多人数で一つのプロジェクトを行っているときを想定しても,スクリプトが書かれたファイルが複数あることは容易に想像できると思います.
上記の場合,メインで実行するファイルと,メインから参照される関数やクラスのスクリプトがあるファイルがありますが,後者をモジュールと呼びます.
ということで,構文も何も無いので,以下の通りモジュール(ファイル)を作成し,参照してみましょう.

まずは,mymodule.py という名前のファイルを,このインタラクティブシェル,もしくは,jupyter を展開しているフォルダに作成します.
次に,このファイルを開き,以下のスクリプトをコピペし,保存します.

class MyCalculator:
    z = 100
    
    def __init__(self, x, y=10):
        self.x = x
        self.y = y
        
    def add_xyz(self):
        return self.x + self.y + self.z
    
    @classmethod
    def get_z_squared(cls):
        return cls.z**2
    
    @staticmethod
    def returns_value_sqrt(value):
        return value**(1/2)

 この出来立てほやほやのモジュールを以下で呼び出します.

from mymodule import MyCalculator

これで,mymodule.py を呼び出すことができました(import, from については,次回の標準関数にて説明いたします).
早速使ってみましょう.

mycalc = MyCalculator(x=1000)
mycalc.add_xyz()
# 1110
MyCalculator.get_z_squared()
# 10000
MyCalculator.returns_value_sqrt(value=42)
# 6.48074069840786

以上が,関数/クラス/メソッド/モジュールについての紹介でした.

今回も以下の通り演習問題と解答を用意しましたので,ぜひチャレンジしてください.

演習問題

以下演習を解くためには,条件分岐,繰り返し処理,内包表記,ファイルからの読み込み,文字列操作をある程度理解している必要があります.

  • Q1. 数値のリスト 1 つを仮引数とし,戻り値をこのリストの RMS 値 1 つとした関数: calc_rms を作成してください.ここで,データの個数を \(n\), i番目のデータを \(x_i\) とすると,RMS 値は,

$$
RMS = \sqrt{\frac{1}{n} \sum_{i=1}^{n}x_i^2}
$$

で計算されます.

  • Q2. Q1 で作成した calc_rms の引数に 1 ~ 100 の奇数のリストを入力し,結果(戻り値)が 57.73214… であることを確認してください.
  • Q3. https://raw.githubusercontent.com/KazutoMakino/PythonCourse/main/006_def_class/data.txt を,今実行しているインタラクティブシェルか jupyter と同じフォルダに保存してください(右クリックして出るメニューで「名前を付けて保存」を選択します). この ./data.txt を読み,戻り値が Humidity[%] の値のみのタプルとした関数: load_humi_data を作成してください.データの読み込みにおいて,import が必要な標準関数やライブラリを用いず,テキストデータとして読み込んだものを処理する関数にしてください.
  • Q4. Q3 の戻り値であるタプルについて,先頭/末尾それぞれ 5 つを表示し,それぞれ (56.44159, 55.97925, 55.46502, 54.99352, 54.72496)(46.95049, 46.89556, 46.91997, 46.92302, 46.86809) であることと,タプルの中身の要素の一つを取り出し,その型が float であることを確認ください.
  • Q5. 以下のクラス定義/メソッド実行を行ってください(結果が合えば良く,細部の差異については気にする必要はありません).
    • Q1 と Q3 で作成した calc_rmsload_humi_data をメソッドに持つクラス: HumidityRMSGetter を作成してください.
    • 時系列データである load_humi_data の戻り値のタプルについて,そのデータ点を含んだ時間方向に対してそれ以前の計 10 点のデータに対する RMS 値を格納したリストを返すメソッド: get_rms_arr を作成してください.このとき,get_rms_arr に渡す配列の最初の方においてデータ点は 10 点に届きませんが,この場合はそのデータ点含むそれ以前のデータ点全てとしてください.また,get_rms_arr にて返す RMS 値は,小数点第5位までとし,小数点第6位に偶数丸めを適用したものとしてください.
    • 最後に,HumidityRMSGetter をインスタンス化し,get_rms_arr を呼び出して結果を変数に代入し,先頭/末尾それぞれ 3 つ表示し,それぞれの結果が [56.44159, 56.2109, 55.96337][46.87241, 46.8774, 46.87726] であることを確認ください.

演習問題の解答

Q1.

def calc_rms(arr):
    # init
    ret = 0
    
    for x in arr:
        # add squared value
        ret += x**2
    
    # mean
    ret /= len(arr)
    
    # sqrt
    ret = ret**(1/2)
    
    return ret

または,

def calc_rms(arr):
    return (sum([v**2 for v in arr]) / len(arr))**(1/2)

Q2.

arr = list(range(1,101,2))
ret = calc_rms(arr=arr)
ret, round(ret, 5)==57.73214
# (57.73214009544424, True)

Q3.

まずは,ファイルの中身を見てみます.
読み込んだ際に全て表示すると大きい場合があるので,読み込んだら,文字数をまず出力してみます.

with open("./data.txt", mode="r") as f:
    txt = f.read()
len(txt)
# 59462

59462 文字ということで,全部表示させると邪魔なので,前半/後半のそれぞれ 500 文字について表示してみます.

txt[:500]
# 'TimeStamp: 2022/01/18 19:29:14.688684, ElapsedTime[s]: 114.311, Temperature[degC]: 19.40032, Humidity[%]: 56.44159\nTimeStamp: 2022/01/18 19:29:16.22105, ElapsedTime[s]: 115.644, Temperature[degC]: 19.38697, Humidity[%]: 55.97925\nTimeStamp: 2022/01/18 19:29:17.355098, ElapsedTime[s]: 116.977, Temperature[degC]: 19.40032, Humidity[%]: 55.46502\nTimeStamp: 2022/01/18 19:29:18.688375, ElapsedTime[s]: 118.31, Temperature[degC]: 19.40032, Humidity[%]: 54.99352\nTimeStamp: 2022/01/18 19:29:20.21160, Elap'
txt[-500:]
# 'e[degC]: 19.6273, Humidity[%]: 46.95049\nTimeStamp: 2022/01/18 19:40:42.663524, ElapsedTime[s]: 802.285, Temperature[degC]: 19.61395, Humidity[%]: 46.89556\nTimeStamp: 2022/01/18 19:40:43.996179, ElapsedTime[s]: 803.618, Temperature[degC]: 19.60059, Humidity[%]: 46.91997\nTimeStamp: 2022/01/18 19:40:45.329131, ElapsedTime[s]: 804.951, Temperature[degC]: 19.61395, Humidity[%]: 46.92302\nTimeStamp: 2022/01/18 19:40:46.660690, ElapsedTime[s]: 806.282, Temperature[degC]: 19.60059, Humidity[%]: 46.86809\n'

ということでデータ形式としては辞書のような形で,変数名と値がコロンで 1 対 1 に対応し,一つのタイムスタンプに対してカンマ区切りでデータが 3 つあり,次のタイムスタンプとデータの登録は \n による改行で区切られています(もし,改行コードが \n 以外に書き換えられていたら,そちらに読み替えてください).
題意の Humidity[%] の値のみのリストを取得するためには,

  • 改行 \n で区切ったリストを作る
  • 上記の各要素について Humidity[%]: を含むこれ以前の文字列を削除し,float でキャストする

上記 2 つの工程を経て実現できると考えられますので,以下のように実装します.

def load_humi_data(fpath="./data.txt"):
    # load data.txt
    with open(fpath, mode="r") as f:
        txt = f.read()
    
    # split: "\n"
    data = txt.split("\n")
    
    # cut and cast to float
    data = tuple(float(v.split("Humidity[%]: ")[-1]) for v in data if v)
    
    return data

Q4.

data = load_humi_data()
data[:5]
# (56.44159, 55.97925, 55.46502, 54.99352, 54.72496)
data[-5:]
# (46.95049, 46.89556, 46.91997, 46.92302, 46.86809)
type(data[0])
# float

Q5.

class HumidityRMSGetter:
    def __init__(self, fpath="./data.txt", smp_rate=10):
        # set source file path
        self.fpath = fpath

        # sampling rate
        self.smp_rate = smp_rate
        
    @staticmethod
    def calc_rms(arr):
        return (sum([v**2 for v in arr]) / len(arr))**(1/2)

    def load_humi_data(self):
        # load data.txt
        with open(self.fpath, mode="r") as f:
            txt = f.read()

        # split: "\n"
        self.data = txt.split("\n")

        # cut and cast to float
        self.data = tuple(
            float(v.split("Humidity[%]: ")[-1]) for v in self.data if v
        )
        
    def get_rms_arr(self):
        # get humidity tuple
        self.load_humi_data()
        
        # init
        rms_arr = []
        
        # loop: range(len(self.data))
        for i in range(len(self.data)):
            # get data into calc_rms
            data = self.data[:i + 1] if i < self.smp_rate else self.data[i - self.smp_rate : i + 1]
                
            # calc rms
            rms = HumidityRMSGetter.calc_rms(arr=data)
            
            # round
            rms = round(rms, 5)
            
            # append
            rms_arr.append(rms)
            
        return rms_arr
    

# instance
hum_rms_getter = HumidityRMSGetter()

# get rms list
rms_arr = hum_rms_getter.get_rms_arr()
rms_arr[:3]
# [56.44159, 56.2109, 55.96337]
rms_arr[-3:]
# [46.87241, 46.8774, 46.87726]

Python のおすすめの学習方法

プログラミングを最短で習得する,少なくても自分の意志で使えるようになる方法について,いくつかプログラミング言語を触ってきた筆者としては何の言語においても,以下2点が重要だと思います.

  • 元々自分が他の言語で作っていた処理を違う言語で書き直す・・・・英語を勉強するときも,脳を生まれたばかりのまっさらな状態から勉強するわけではなく,日本語を通したり対比して,学習済みの言語野を用いて勉強するのと似ています
  • 言語自体を網羅的に勉強するのではなく,やりたい事を先に考え,それを達成するために色々と調べながら実装する・・・・例えば,留学で語学力が上達するのは,その国の言葉を使ってコミュニケーションを取ることが強制されるためであり,使うことに対するモチベーションが一番大事です

独学で行うには,やはり2点目の「やりたい事ドリブン学習」が効果的で,例えば次の書籍は,Python を流行らせている AI/データ分析/機械学習/深層学習について実装することに主眼を置き説明されているので,実際に手を動かしながら学んでいける本だと思います(筆者も最初にこちらの書籍で遊びながら学びました).

コメント

タイトルとURLをコピーしました