5.2 ユーザー定義関数

(1/1)
ユーザー定義関数のイメージ
Python では、組み込み関数標準ライブラリ外部ライブラリにないが、よく使う処理を関数に書くことができる。これをユーザー定義関数と呼ぶ。
今回は、作成したユーザー定義関数を使ってグラフを描いたり、書いてはいけない関数について学ぶ。

目次

サンプル・プログラム

ユーザー定義関数

すでに何度も出てきたが、ユーザー定義関数
def 関数名(引数1, 引数2...)
    処理1
    処理2
    ...
    return 戻り値
のように書く。
2.2 繰り返し処理」で紹介した1から10の和を求める計算式を一般化し、aからbまでの自然数の和を求めるユーザー関数を作ってみることにする。
def sumNaturalNumbers1(a, b):
	"""aからbまでの自然数の和を求めるユーザー関数
	
	Args:
		a(int): 開始値
		b(int): 終了値

	Returns:
		int 合計値
	"""
	sum = 0
	for i in range(a, b + 1):
		sum += i
	return sum
プログラム "sumNaturalNumbers1.py" を実行してみてほしい。ユーザー関数 sumNaturalNumbersblue1 は、引数2つ(開始値、終了値)で作用はない関数である。
ここで計算した自然数の並びを等差数列と呼ぶが、応用として、等比数列の和を求めるユーザー関数を作ってみてほしい。
三角形の面積を求める
次に三角形の面積を求める公式 底辺×高さ÷2 をユーザー関数にしてみる。
def calcAreaTriangle(base, height):
	"""三角形の面積を求める.
	
	Args:
		base(float)		底辺
		height(float)	高さ
	
	Returns:
		float	三角形の面積
	"""
	area = base * height / 2
	return area
プログラム "calcAreaTriangle.py" を実行してみてほしい。
三角形の面積を求めるユーザー定義関数 calcAreaTriangle は、引数2つ(底辺、高さ)で作用はない関数である。
応用として、長方形や台形の面積を求めるユーザー関数を作ってみてほしい。

戻り値のないユーザー定義関数

ユーザー定義関数で return を書かないと、戻り値はない。"printBoolJa1.py" を実行してみてほしい。
def printBoolJa(x):
	"""真偽値を日本語で表示するユーザー関数
	
	Args:
		x(bool): 真偽値
	
	Returns:
		None
	"""
	if (x):
		print("真")
	else:
		print("偽")
ユーザー関数 printBoolJa は、引数2つで戻り値はなく、結果を表示する作用をともなう関数である。
では、ユーザー関数 printBoolJa の戻り値を取得しようとするとどうなるだろうか。"printBoolJa2.py" を実行してみてほしい。
# 真偽値を日本語で表示する.
d = printBoolJa(c)
# 戻り値を表示してみる.
print(d)
結果は None(値がない)となる。

関数とグラフ

5.1 組み込み関数とモジュール」で2次関数のグラフを描いたが、ユーザー定義関数を使うことで、メインプログラムはそのままに、さまざまな関数のグラフを描くことができる。「 5.1 組み込み関数とモジュール」で導入した外部ライブラリ Matplotlib と科学技術計算ライブラリ NumPy が必要になる。
def cubicFunction(a, b, c, d, x):
	"""3次関数
	Args:
		a, b, c, d(float)	係数
		x(float)			変数
	Returns:
		float	関数値
	"""
	return a * x ** 3 + b * x ** 2 + c * x + d
	xList = np.arange(X_MIN, X_MAX, 0.1).tolist()
	yList = [cubicFunction(1, 0, -2, 0 , x) for x in xList]
3次関数のグラフ
プログラム "cubicFunction.py" を実行すると、左図のようなグラフを表示する。これは3次関数のグラフを描くプログラムだ。3次関数をユーザー定義関数 cubicFunction に書いた。
一方、メインプログラムの y値のリスト yList を見てほしい。先ほどの3次関数 cubicFunctionfor文で繰り返してリストを生成している。つまり、ここに書くユーザー定義関数を変えるだけで、さまざまな関数のグラフを描くことができる。
応用として、適当なユーザー関数を定義し、そのグラフを描いてみてほしい。

書いてはいけない関数

数学で言うところの関数とは、ある引数を渡すと、戻り値が一意に決まるものである。これを、プログラミングでは関数の参照透過性と呼ぶ。
一方で、同じ引数を渡しても、何らかの要因によって戻り値が変化するものは関数とは呼ばない。

ここで問題になるのが、関数の中で外部変数やAPI、インターフェースなどを参照し、その結果によって戻り値が異なる場合だ。引数に連動して外部変数やAPI、インターフェースからの応答が異なるような仕組みであれば参照透過性は担保できるが、そうでないものは参照透過性がないということになる。参照透過性がないと、単体テストが出来ない。
from datetime import date

def isLeapNow():
	"""今年はうるう年かどうかを判定するユーザー関数
	
	Args:
		None
	
	Returns:
		bool True:うるう年 / False:平年
	"""
	# 現在の西暦年
	year = date.today().year
	# うるう年かどうか判定する
	if (year % 400 == 0):
		return True
	elif (year % 100 == 0):
		return False
	elif (year % 4 == 0):
		return True
	else:
		return False

# メイン・プログラム =======================================================
プログラム "isLeapNow.py" を実行してほしい。ユーザー定義関数 isLeapNow は、今年がうるう年かどうかを判定するものだが、一見すると、正しく動作している。
しかし、こういったユーザー定義関数を書いてはいけない。なぜなら、ユーザー定義関数 isLeapNow に透過性がないからだ。

ユーザー定義関数 isLeapNow は、引数がないのに戻り値が一定しない。これは、関数外で定義されている変数 date.today().year を参照しているためだ。
isLeapNow は正しい動きをする。しかし、関数外で定義されている変数によって戻り値が変わるため、単体テストすることができない。
単体テストとは、関数にテスト用の引数をいくつか(※)代入し、正しい戻り値が返るか、正しい作用をするかをみるテストのことで、ユーザー定義関数ができたら最初に行うテストである。(※関数の設計から、境界条件になるようなテスト引数をあらかじめ用意する。)
このあとに結合テスト、運用テストを行うのだが、プログラムはこれらのテストに合格して品質を担保できてから、はじめて出荷できる。逆に言えば、透過性がない関数は単体テストできないので、書いてはいけないのである。

では、どうすれば単体テストができるプログラムになるだろうか。これは宿題として取り組んでほしい。

練習問題

次回予告

数学には繰り返し制御の数式がなく、漸化式で表すことが多い。漸化式を素直に Pythonプログラムで書こうとすると、再帰呼び出しという手法を使う。
次回は、1から10の和や、複利計算、ニュートン近似法を再帰呼び出しで書き直してみる。また、自然界によく見られるフラクタル図形を描くのにも再帰呼び出しが利用できる。

コラム:Pythonプログラムのexe化

1.2 実行環境と開発環境」で紹介したように、WindowsでPythonプログラムを実行するには、Python実行環境をインストールする必要がある。さらに外部モジュールを利用したプログラムでは、外部モジュールを追加インストールしなければならない。
WindowsユーザーはパソコンやOS、プログラミング言語に詳しい人ばかりとは限らないので、Pythonプログラムを配布する際には少し工夫が要る。

Pyinstaller という外部ライブラリを使うと、Pythonプログラムを Windows上で即実行可能な exeファイルに変換することができる。
exe化といっても、Pythonスクリプトと実行環境を1つのexeファイルにまとめただけなので、処理が高速化するわけではない。また、Python 3.7以上の実行環境が必要だ。32ビット実行環境で作ったexeファイルは32ビットと64ビット環境の両方で動くが、64ビット実行環境で作ったexeファイルは64ビット環境でしか動かない。導入しているセキュリティ対策ソフトによっては、マルウェアと誤認して動かない場合がある。
Pyinstaller は外部ライブラリの一種なので、
pip install pyinstaller
でインストールする。
インストールできたら、
pyinstaller [exe化したいpyファイル名] --noconsole --onefile
とすると、distディレクトリのexeファイルができあがる。オプション "--noconsole" は DOS窓を表示させないようにするもの、"--nonefile" は1つの exeファイルにするもの。
インストールされている外部ライブラリもexe化できるので、たとえば「パスワード生成機 - 1.4 ブラウザを使ったGUI」で紹介した "makePassword.py" をexe化すると、簡単なGUIアプリができあがる。
さらにオプション "--icon=アイコンファイル.ico" を指定すると、できあがったexeファイルにオリジナル・アイコンを付けることができる。
(この項おわり)
header