見出し画像

Pythonのクラス/関数のモジュール化

こんにちは(@t_kun_kamakiri)

Pythonで書いた自分のコードをモジュール化することを考えます。

自分で書いたコードは機能ごとに関数やクラスにまとめておくとコードを再利用するときには利便性が増します。

ということで、あまり今までPythonのコードのモジュール化をしたことがなかったので練習がてらコードを書いてみました。

こちらの参考書を見ながらコードを実行していきます。

本記事のコードはgithubから入手できます。

モジュールの定義

まずは普通に関数を書いてmain()関数を実行します。

test001/main.pyの中身を以下とします。

import math

def get_trialgle(base, height):
   """
   三角形の面積を計算
   base: 底辺[m]
   height: 高さ[m] 
   """
   return base * height /2

def get_circle(radius):
   """
   円の面積を計算
   Area : 円の面積[m]
   radius: 半径[m]
   """
   Area = radius * radius *math.pi
   return Area

if __name__ == '__main__':
   print(f'__name__ は{__name__}となっている。')
   print(f'三角形の面積は{get_trialgle(10, 2)}')
   print(f'円の面積は{get_circle(10)}')

コードを実行します。

PS C:\work\Python\20210226_Python_module\test001> python .\main.py
__name__ は__main__となっている。
三角形の面積は10.0
円の面積は314.1592653589793

if __name__ == '__main__':
の「__name__ 」はPythonの特殊な変数でモジュールが実行された際には「__main__」となっています。つまり実行されたメインのファイルなら「if __name__ == '__main__':」 のif文を満たすので、if文の中の処理が実行されるということです。

モジュールのインポート

では、main.pyに書いた関数を別のファイルに保存しておき、それをモジュールとして呼び出すようにします。

ファイル構成はこのようにしておきます。

画像1

get_math.py

import math

def get_trialgle(base, height):
   """
   三角形の面積を計算
   base: 底辺[m]
   height: 高さ[m] 
   """
   return base * height /2

def get_circle(radius):
   """
   円の面積を計算
   Area : 円の面積[m]
   radius: 半径[m]
   """
   Area = radius * radius *math.pi
   return Area


print('-'*10 + f'{__name__}' + '-'*10)
print(f'__name__ は{__name__}となっている。')

これをmian.pyで読み込むようにするは「from モジュール名 import 関数orクラス」と書きます。

main.py

from get_math import get_trialgle, get_circle

if __name__ == '__main__':
   print('-'*10 + f'{__name__}' + '-'*10)
   print(f'__name__ は{__name__}となっている。')
   print(f'三角形の面積は{get_trialgle(10, 2)}')
   print(f'円の面積は{get_circle(10)}')

【結果】

PS C:\work\Python\20210226_Python_module\test002> python .\main.py
----------get_math----------
__name__get_mathとなっている。
----------__main__----------
__name__ は__main__となっている。
三角形の面積は10.0

get_math.pyの中にも「print(f'__name__ は{__name__}となっている。')」と記述しておきました。
結果を見ての通り「__name__ はget_mathとなっている。」という結果になっており、importされたファイルでは__name__はそのファイル名になっていることがわかります。

※ちなみにmain.pyは呼び出すモジュールの中の関数を複数指定する場合は「from get_math import *」のように書けます。

画像3

main.py

from get_math import *

if __name__ == '__main__':
   print('-'*10 + f'{__name__}' + '-'*10)
   print(f'__name__ は{__name__}となっている。')
   print(f'三角形の面積は{get_trialgle(10, 2)}')
   print(f'円の面積は{get_circle(10)}')

ただ、モジュールが複数ある場合にはどのモジュールに「get_trialgle」が含まれていたのかがわからなくなるのであまりよくない記述かと思います。

また、main.pyを以下のように「import get_math」としてモジュール名だけ書いてもよいです。

画像4

import get_math

if __name__ == '__main__':
   print('-'*10 + f'{__name__}' + '-'*10)
   print(f'__name__ は{__name__}となっている。')
   print(f'三角形の面積は{get_math.get_trialgle(10, 2)}')
   print(f'円の面積は{get_math.get_circle(10)}')

「get_math.関数」のようにドットでつなげていきます。

また、毎回「「get_math.」と書くのが面倒な場合は呼び出し名を変更することもできます。

画像5

import get_math as g

if __name__ == '__main__':
   print('-'*10 + f'{__name__}' + '-'*10)
   print(f'__name__ は{__name__}となっている。')
   print(f'三角形の面積は{g.get_trialgle(10, 2)}')
   print(f'円の面積は{g.get_circle(10)}')

このようにすれば「g.get_trialgle(10, 2)」とget_mathの代わりにgと書くだけでよいということになります。

別のファイルにモジュールをまとめる

ファイル構成を以下のようにしておきます。

画像2

myget_math/get_math.py

import math

def get_trialgle(base, height):
   """
   三角形の面積を計算
   base: 底辺[m]
   height: 高さ[m] 
   """
   return base * height /2

def get_circle(radius):
   """
   円の面積を計算
   Area : 円の面積[m]
   radius: 半径[m]
   """
   Area = radius * radius *math.pi
   return Area


print('-'*10 + f'{__name__}' + '-'*10)
print(f'__name__ は{__name__}となっている。')

main.py

from myget_math import get_math

if __name__ == '__main__':
   print('-'*10 + f'{__name__}' + '-'*10)
   print(f'__name__ は{__name__}となっている。')
   print(f'三角形の面積は{get_math.get_trialgle(10, 2)}')
   print(f'円の面積は{get_math.get_circle(10)}')

これで「myget_math」のパッケージ↓の「get_math.py」モジュールにを利用することができます。

原理を考えるであればmain.pyを以下のようにしてもよいと考えられます。

画像6

main.py

import myget_math.get_math

if __name__ == '__main__':
   print('-'*10 + f'{__name__}' + '-'*10)
   print(f'__name__ は{__name__}となっている。')
   print(f'三角形の面積は{myget_math.get_math.get_trialgle(10, 2)}')
   print(f'円の面積は{myget_math.get_math.get_circle(10)}')

「myget_math.get_math」のように「パッケージ.モジュール」で読み込ませて使う際も「myget_math.get_math.get_trialgle(10, 2)」のように書けばいいわけです・・・が面倒ですよね。

長い「import myget_math.get_math」を「g」と置き換えればすっきりします。

画像7

main.py

import myget_math.get_math as g

if __name__ == '__main__':
   print('-'*10 + f'{__name__}' + '-'*10)
   print(f'__name__ は{__name__}となっている。')
   print(f'三角形の面積は{g.get_trialgle(10, 2)}')
   print(f'円の面積は{g.get_circle(10)}')

パッケージを初期化する

ただ「import myget_math.get_math as g」自体が長いので「myget_math」の中にパッケージを初期化する「__init__.py」を置いてその中に使用するモジュールを書いておきます。

画像8

「myget_math/__init__.py」

from myget_math import get_math

print('====実行された====')

このように書いておくことでmain.pyが実行された時点で「__init__.py」が読み込まれて上記のパッケージとモジュールを指定して使うことができます。

main.py

import myget_math

if __name__ == '__main__':
   print('-'*10 + f'{__name__}' + '-'*10)
   print(f'__name__ は{__name__}となっている。')
   print(f'三角形の面積は{myget_math.get_math.get_trialgle(10, 2)}')
   print(f'円の面積は{myget_math.get_math.get_circle(10)}')

このようにmain.pyの中にもパッケージを指定するだけで良いので記述がすっきりします。

「myget_math/get_math.py」は特に変更はなし。

import math

def get_trialgle(base, height):
   """
   三角形の面積を計算
   base: 底辺[m]
   height: 高さ[m] 
   """
   return base * height /2

def get_circle(radius):
   """
   円の面積を計算
   Area : 円の面積[m]
   radius: 半径[m]
   """
   Area = radius * radius *math.pi
   return Area


print('-'*10 + f'{__name__}' + '-'*10)
print(f'__name__ は{__name__}となっている。')

【結果】

PS C:\work\Python\20210226_Python_module\test009> python .\main.py
----------myget_math.get_math----------
__name__ はmyget_math.get_mathとなっている。
====実行された====
----------__main__----------
__name__ は__main__となっている。
三角形の面積は10.0
円の面積は314.1592653589793

main.pyを実行すると「import myget_math」➡「__init__.pyの中のfrom myget_math import get_math」が読み込まれ➡「print('====実行された====')」が実行されたという流れで実行されています。
「import myget_math」を実行しただけで「myget_math」のパッケージ内の「__init__.py」を読みに行っているというのがわかります。

次のようにモジュールが複数になってくると以下のように「__init__.py」に読み込むモジュールを記述しておきます。

画像9

「myget_math/__init__.py」

from myget_math import get_math
from myget_math import get_math_multi

print('====実行された====')

「myget_math/get_math.py」

import math

def get_trialgle(base, height):
   """
   三角形の面積を計算
   base: 底辺[m]
   height: 高さ[m] 
   """
   return base * height /2
   
def get_circle(radius):
   """
   円の面積を計算
   Area : 円の面積[m]
   radius: 半径[m]
   """
   Area = radius * radius *math.pi
   return Area

print('-'*10 + f'{__name__}' + '-'*10)
print(f'__name__ は{__name__}となっている。')

「myget_math/get_math_multi.py」←追加

import math

def get_trialgle2times(base, height):
   """
   四角形の面積は三角形の面積の2倍
   base: 底辺[m]
   height: 高さ[m] 
   """
   triangle = base * height /2

   return 2 *triangle


print('-'*10 + f'{__name__}' + '-'*10)
print(f'__name__ は{__name__}となっている。')

【結果】

PS C:\work\Python\20210226_Python_module\test010> python .\main.py
----------myget_math.get_math----------
__name__ はmyget_math.get_mathとなっている。
----------myget_math.get_math_multi----------
__name__ はmyget_math.get_math_multiとなっている。
====実行された====
----------__main__----------
__name__ は__main__となっている。
三角形の面積は10.0
円の面積は314.1592653589793
四角形の面積は20.0

結果はこのようになりました。

「__init__.py」の中身は以下のようにパッケージを相対位置として読み込ませてもよいです。

画像10

「myget_math/__init__.py」

from . import get_math
from . import get_math_multi

print('====実行された====')

クラスにまとめておく

ファイルを複数にわけるのも面倒なので計算をさせる機能をひつのファイルにまとめて計算する機能を関数としてクラスの中に入れておきます。

画像11

「myget_math/__init__.py」

from .get_math_multi import CalcMath

print('====実行された====')

「myget_math/get_math_multi.py」←この中にクラスを作ります。

import math

class CalcMath:
   def __init__(self, base, height, radius):
       self.base = base
       self.height = height
       self.radius = radius

   def get_trialgle(self):
       """
       三角形の面積を計算
       base: 底辺[m]
       height: 高さ[m] 
       """
       return self.base * self.height /2

   def get_trialgle2times(self):
       """
       四角形の面積は三角形の面積の2倍
       base: 底辺[m]
       height: 高さ[m] 
       """
       triangle = self.base * self.height /2

       return 2 *triangle        
   def get_circle(self):
       """
       円の面積を計算
       Area : 円の面積[m]
       radius: 半径[m]
       """
       Area = self.radius * self.radius *math.pi
       return Area

print('-'*10 + f'{__name__}' + '-'*10)
print(f'__name__ は{__name__}となっている。')

これをmain.pyからクラスを読み込むようにして、各関数を呼び出せばよいですね。

main.py

import myget_math

if __name__ == '__main__':
   calc = myget_math.get_math_multi.CalcMath(10, 100, 1000)

   print('-'*10 + f'{__name__}' + '-'*10)
   print(f'__name__ は{__name__}となっている。')
   print(f'三角形の面積は{calc.get_trialgle()}')
   print(f'四角形の面積は{calc.get_trialgle2times()}')
   print(f'円の面積は{calc.get_circle()}')

このようにするとmainファイルはすっきり書くことができます。

【結果】

PS C:\work\Python\20210226_Python_module\test012> python .\main.py
----------myget_math.get_math_multi----------
__name__ はmyget_math.get_math_multiとなっている。
====実行された====
----------__main__----------
__name__ は__main__となっている。
三角形の面積は500.0
四角形の面積は1000.0
円の面積は3141592.653589793

あまりクラスの使い方は慣れていないですが、プログラムの機能が多くなりすぎた場合は機能ごとに一度整理をしたほうが再利用する際には便利になりますね。

追記:記事にまとめました

noteの内容をブログ記事にまとめました。

【Python初心者】別ファイルの関数やクラスの実行(モジュール化)

Twitter➡@t_kun_kamakiri
Instagram➡kamakiri1225
ブログ➡宇宙に入ったカマキリ(物理ブログ)
ココナラ➡物理の質問サポートサービス
コミュニティ➡製造業ブロガー

この記事が気に入ったらサポートをしてみませんか?