[Python] ユーザー定義関数

ここでは、pythonプログラミングの基本である、関数について解説します。まずは、Google Colaboratoryの使い方 を参考に、プログラミングの環境に移動しましょう。ローカルに環境を構築している方は、そちらでもokです。

関数とは

これまで、

  • print関数: ディスプレイに変数の値などを表示する機能
  • len関数: リストなどの長さを返す機能
  • type関数: 変数の型を返す機能

などを扱ってきました。プログラミングの世界では、このような機能を関数と呼びます。関数には大きく分けて、

  • 返り値のある関数
  • 返り値のない関数

があります。これを理解するために、次のコードを実行してみましょう。

val = 230
a = print(val)
b = type(val)

print("a=", a)
print("b=", b)

イコールは右辺の値を左辺に代入するという役割でした。そのため、aにはprint関数が、bにはtype関数が代入されています。これを実行すると、

  • aの中身 → None
  • bの中身 → int

が表示されたと思います。文字通り、aの中には何も入っていません、bの中にはintが入っています、ということを意味しています。つまり、2行目の右辺にある「print(val)」は何ものでもないので、aに何も代入されないわけです。一方で、3行目の右辺にある「type(val)」は int という値をbに代入させています。このように、

  • 何も値を返してくれない関数(イコールの右辺においても、左辺に何も代入されない) → 返り値のない関数
  • 何らかの値を返してくれる関数(イコールの右辺におくと、左辺に値が代入される) → 返り値のある関数

と呼びます。print関数は単にディスプレイに表示させる機能であるため、何の返り値もありませんから、返り値のない関数となります。一方、type関数は型を返してくれますから、返り値のある関数となります。

さて、少し話は変わりますが、print関数やtype関数なのように事前に用意されている関数のことを、組み込み関数と呼びます。組み込み関数は大変便利ですが、大きな開発を行う場合は、独自に関数を作っておいて、何度も使い回すことが普通です。このように、ユーザーが独自に作った関数のことを、ユーザー定義関数と呼びます。ここでは、ユーザ定義関数の作り方について説明します。

返り値のない関数

まず、返り値のないユーザ定義関数の作り方について説明します。この必要性を理解するために、以下のコードを見てください。以前、if分の解説で使用したコードです。

s = "男"
weight = 50

if s == "男":
 if weight < 40 or weight > 100:
  print("体重が異常値です。")
  print("医師の検診をお勧めします。")
 else:
  print("体重は正常です。")

elif s == "女":
 if weight < 30 or weight > 90:
  print("体重が異常値です。")
  print("医師の検診をお勧めします。")
 else:
  print("体重は正常です。")

これを見たとき、4〜9行目と11〜16行目がとても似ていると感じないでしょうか。このように、同じような書き回しが必要な箇所で、毎回いちいち最初から書いていると、とても非効率のように思えます。この冗長性を解消するため、以下の記述を行います。

def WeightChecker(Your_weight, Min_weight, Max_weight):
 if Your_weight < Min_weight or Your_weight > Max_weight:
  print("体重が異常値です。")
  print("医師の検診をお勧めします。")
 else:
  print("体重は正常です。")

これを実行しても、何も起こりません。単にこのような関数を定義する、という処理なだけで、この関数を使っていないためです。

defはこれから関数を定義しますという識別子で、必ず記載が必要です。その先にあるWeightCheckerは関数の名前で、基本的に自由に命名することができます。WeightCheckerの右に丸カッコがあり、その中に

  • Your_weight
  • Min_weight
  • Max_weight

という記載があります。これは、引数と呼ばれるもので、上から順に第1引数〜第3引数と言います。このように書くことで、WeightChecker関数は、3つの値を入れなければ動かないことが約束されます。もしこの関数を使うときに

  • WeightChecker(50, 40, 100)

と書けば、Your_weigthを50、Min_weigthを40、Max_weigthを100として、WeightChecker関数が機能します。50は40未満、あるいは、50は100より大きい、いずれも満たしませんので、else文の処理が実行され、「体重は正常です。」と表示されます。

これがわかったところで、上記の関数を使ってみます(上の関数を実行していないと、そのような関数は存在しませんというエラーが出るので、必ず定義した関数を実行してから、以下のコードを実行してください。)。

s = "男"
weight = 50

if s == "男":
  WeightChecker(weight, 40, 100)

elif s == "女":
  WeightChecker(weight, 30, 90)

sは男であり、weigthは50ですから、5行目のWeightChecker(50, 40, 100) が実行されることになります。50kgは40kg未満でもなく100kgより大きくもないので、正常と表示されます。このように、本来5行書かなければならなかった記述を、関数を使うことで、わずか1行で記載することができました。

ちなみにこの関数は、以下のように性別の条件式まで組み込むこともできます。

def WeightChecker_s(Your_s, Your_weight, Min_weight_man, Max_weight_man, Min_weight_woman, Max_weight_woman):
 if Your_s == "男":
  if Your_weight < Min_weight_man or Your_weight > Max_weight_man:
    print("体重が異常値です。")
    print("医師の検診をお勧めします。")
  else:
    print("体重は正常です。")

 if Your_s == "女":
  if Your_weight < Min_weight_woman or Your_weight > Max_weight_woman:
    print("体重が異常値です。")
    print("医師の検診をお勧めします。")
  else:
    print("体重は正常です。")

一応、機能が異なる関数なので、関数名をWeightChecker_sに変更しています。この場合は、以下のように書けばokです。

s = "男"
weight = 50
WeightChecker_s(s, weight, 40, 100, 30, 90)

読み方は先ほどと同じです。今回は引数が6つもあるのでややこしいですが、落ち着いて読めば理解できるはずです。

以上が、返り値のない関数の定義方法です。

  • a = WeightChecker(40, 100, 300)

のような使い方をしないのはわかりますか?仮にこう書いた場合、aにNoneが代入されます。単に結果を表示したいだけの場合は、何かの変数に代入させたいという動機は湧きません。このようなとき、返り値のない関数を定義します。

ちなみに、少し複雑だと感じる場合は、以下のようなシンプルな関数で、返り値のない関数を理解してみても良いと思います。

def add_two_values(fa, fb):
 fc = fa + fb
 print(fc)

a = 10
b = 20
add_two_values(a, b)

これは、入力された2つの数字を足した結果を表示する関数です。ちなみに、関数の範囲もifやforと同様に、インデントがされている箇所までとなります。上のコードの場合、3行目でadd_two_values関数の定義が終了します。5行目以降は、関数の外側の処理となります。

もう一点補足です。関数は、必ずしも入力の変数が必須なわけではありません。以下は、入力のない関数です。

def print_3_dams():
 print("dam")
 print("dam")
 print("dam")

print_3_dams()

入力のない関数を定義する場合は、丸カッコの中身を空白にすればokです。

返り値のある関数

続いて、返り値のある関数について説明します。これを理解するため、以下のコードを見てみましょう。

def additions_3_values(fa, fb, fc):
 res = fa + fb + fc
 return res

additions_3_valuesという名前の関数があり、3つの値を入力すると、それらが足し合わせるというところまでは理解できると思います。ただし、最後に return res という新しい表記があることがわかります。これは、「resを返す」という意味があります。返すとは、先ほど説明した通り、

  • a = additions_3_values(10, 20, 30)

とかくと、aの中に返り値が代入されることを意味します。実際にこれをチェックしてみます。

a = additions_3_values(10, 20, 30)
print(a)

初学者にありがちなミスとして、以下の事例があります。

def additions_3_values_miss(fa, fb, fc):
 res = fa + fb + fc

a = additions_3_values_miss(10, 20, 30)
print(a)

aに60が代入されていると思いきや、aをprintするとNoneと返ってきます。これは、additions_3_values関数が、resを返していないためです(よく見ると、return res がない)。返り値がある・ないの意味をしっかりと理解するようにしましょう。

少し応用事例として、リストのすべての要素を足し合わせ、それを返り値とする関数を作ってみましょう。

def sum_list(list_f):
 n = len(list_f)
 sumval = 0

 for i in range(n):
  sumval = sumval + list_f[i] 
 
 return sumval

以下のコードで実行できます。

LIST_A = [10, 20, 30]
sum_res_A = sum_list(LIST_A)
print(sum_res_A)

LIST_B = [10, 500, 20, 2.32, 1.98, 1000.21, 20000]
sum_res_B = sum_list(LIST_B)
print(sum_res_B)

どうしてsum_list関数により、リストの要素の総和が計算されるのか考えてみます。sum_list関数の冒頭にある

  • n = len(list_f)

は、for文の繰り返し回数に利用することはわかると思います。ここで、仮にリストの長さが3、つまりn=3だったとします。この場合、sum_list関数のfor文を、あえてfor文を使わずに書き下せば、

  • sumval = 0
  • sumval = sumval + list_f[0](i=0のとき)
  • sumval = sumval + list_f[1](i=1のとき)
  • sumval = sumval + list_f[2](i=2のとき)

となることがわかります。sumvalがイコールの右辺と左辺にあって、混乱するかもしれません。この場合、イコールは右辺と左辺が等しいという算数や数学のことを一旦忘れましょう。イコールはあくまで右辺の値を左辺に代入する機能であることを思い出すと、sumvalにlist_fの各要素を足したものを新たなsumvalとする、という記述になることが理解できると思います。これを踏まえ、右辺のsumvalを、あえてsumvalを使わずに書くと、以下のようになります。

  • sumval = 0
  • sumval = 0 + list_f[0] (i=0のとき)
  • sumval = 0 + list_f[0] + list_f[1] (i=1のとき)
  • sumval = 0 + list_f[0] + list_f[1] + list_f[2] (i=2のとき)

for文が進むたびに更新されていくsumvalを一つ一つ丁寧に追っていくと、わかると思います。結局、for文を抜ける段階では、リストのすべての要素を足し合わせたものがイコールの右辺に登場するわけです。したがって、sumvalにリストの要素の総和が代入され、それがreturnにより返されることになります。

関数を定義する場合の注意点

以下のコードを見てみましょう。

def func_abc(a, b, c):
 sum = a + b + c
 return sum

a = 10
b = 20
c = 30
d = func_abc(a, b, c)

単純に、3つの数字を足して返却する関数と、それを利用するコードです。しかし、実はこれ、エラーは出ないものの、あまりよくないコードです。関数の内側と外側で、まったく同じ名前の変数が利用されていることがわかるでしょうか。関数の中と外の変数は、わざわざ一致させる必要はない、というよりも、まったくの別物として扱われます。ですので、関数の中と外でまったく同じ名前の変数を用意した場合、その変数が何なのかわからなくなる場合があります。具体的にいうと、このコードは、aにaを代入する、bにbを代入する、cにcを代入する、そして、関数を動かす、というわかりにくい書き方になっています。

このため、必ず、関数の中で使用した変数は、関数の外では使わない、ということをなるべく守るようにしましょう。修正版が下記となります。

def func_abc(fa, fb, fc):
 sum = fa + fb + fc
 return sum

a = 10
b = 20
c = 30
d = func_abc(a, b, c)

命名はなんでも良いですが、関数(function)の中の変数なので、前にfをつけてみました。このように書くと、faにaを代入する、fbにbを代入する、fcにcを代入する、その上で関数を動かす、というように関数の中と外の変数を区別されていることがわかると思います。エラーは出ないものの、一種のマナーですので、ぜひ覚えるようにしてください。

以上で関数を終わります。自分で色々なユーザー定義関数を作ってみましょう。