プログラミングでは、関数を使いこなせるようになると、以下のような大きなメリットを得ることができます。
- ある作業の手間を省略できる。
- 同じコードを何度も書く必要がなくなる。
つまり、プログラムを書くことが遥かに効率的で簡単になるということです。
Pythonでは def 構文を使って、必要な関数を自由に作成することができます。
関数を知れば知るほど、美しく簡潔なコードを書けるようになります。それは、作業効率の向上や、チームとしてコードを書き上げる時の相互理解の易しさに大きく影響します。
ぜひ、ここで、def文を使った関数の作り方を、しっかりと理解しておきましょう。
1. Pythonの関数とは
関数とは、「データを入力すると、定義された通りの処理を実行して出力する一連の命令群」のことです。
ちょうど下図のようなイメージですね。
こうして、関数にデータを入力し、命令を実行し、結果を出力する一連の流れのことを「関数を呼び出す」と言います。そして、関数へ入力するデータのことを「引数」と呼び、関数にデータを入力することを「引数を渡す」と言います。
Pythonの関数は、数学の代数で扱う関数と似ています。
例えば、中学生で習う1次関数「y = 2x」を思い浮かべてみてください。yの値はxの値によって変化し、xの値が与えられるとyの値が定まります。このような関係を「yはxの関数である」といいます。
そのため、数学ではyをf(x)に替えて「 f(x) = 2x」と表します。この場合、f(2)は4、f(10)は20というように、関数に与えるxの値によって、出てくる数値が異なります。
「y = 2x + b」という関数の場合は、yの値は、xの値とbの値によって変化します。そのため「f(x, b) = 2x + b」と表します。例えば、f(4, 5)なら、13になりますね。
pythonでも、以下のように、数学の代数とほとんど同じ式で関数を呼び出します(引数をいくつ渡すかは、関数によって異なります)。
関数名(引数, 引数)
2. def文の書き方(独自関数の作成)
Pythonではdef文を使って関数を作ります。以下がdef文の基本書式です。
def 関数名(引数1, 引数2, ..., 引数n):
'''
docstring(関数の説明文)
'''
関数定義(処理文)
関数定義(処理文)
return 戻り値
まずは構成から解説します。
def文
defはdefinition(=定義という意味)の頭の3文字を取ったもので、これによって、これから関数を定義することをPythonに伝えます。
関数名
defの後に半角スペースを空けて、関数名を書きます。Pythonの関数の命名規則として、大文字を使わずに単語間はアンダースコア(_)で区切ります。
引数(任意)
丸括弧 () の中に引数を入力します。引数を複数設定する場合はカンマで区切ります。作成したい関数によって、引数がある場合とない場合があります。詳細は後述します。def文の最後にコロン( : )を書き、その下からは半角スペース4つのインデントを入れて書いていきます。
docstring(説明・任意)
ここに第三者から見て分かりやすいように、関数の説明を書きます(任意)。誰から見てもわかるような簡単な関数の場合は不要です。なお、”docstring”は’document’と’string’(ドキュメント文字列)の略です。
関数定義(処理文)
ここに関数の機能を定義する処理文を書いていきます。
return(戻り値/返り値・任意)
処理文の最後に、returnを書いて、そこに、この関数を呼び出したときに何を出力するかを定義します。これも作成したい関数によって、ある場合とない場合があります。詳しくは後述します。
それでは、次から実際に関数を作っていくことで、理解を深めていきましょう。
3. 戻り値(return文)がある関数とない関数
まずは、引数がないシンプルな関数で戻り値(return文)があるパターンとないパターンの2つを見てみましょう。
3.1. 戻り値のある関数
それでは、戻り値のある関数を作ってみましょう。
以下のコードをご覧ください。まず、引数のない関数を作るには丸括弧 () を空白にします。そして関数定義の処理文では、return文で戻り値”hello”を指定しています。
def greeting():
return "hello"
この関数を呼び出すにはgreeting()と書きます。
greeting()
関数を呼び出すと文字列の’hello’が返されています。
この戻り値は変数に代入することができます。以下のコードでは、変数aに戻り値を代入してprint()で出力しています。
a = greeting()
print(a)
このようにreturn文で戻り値を定義しておくと、関数を呼び出した時にそれが返ってきます。
戻り値は変数に代入することができます。その変数aをprint()で出力するとhelloが表示されます。
3.2. 戻り値のない関数
次に、戻り値 がない関数を見てみましょう。
以下のコードをご覧ください。
先ほどと同じgreeting()という関数ですが、今度は関数定義の処理文ではreturn文を使わずにprint()を使っています。
def greeting():
print("hello")
この関数を呼び出すと、指定の処理がそのままが実行されます。
greeting()
一見変わらないように見えますが、この関数には戻り値がありません。そのため関数を呼び出して別の変数に代入して出力してもNone(=何もない)になります。
a = greeting()
print(a)
このように、return文で書いた処理は、その関数を呼び出した時に、戻り値として出てきます。それ以外の場合は、処理文が実行されるだけで戻り値はありません。
これが戻り値(return)がある関数とない関数の違いです。
4. 引数がある関数
上の例では引数がない関数を見ましたので、次に引数がある関数を見ていきましょう。
4.1. 引数がある関数を書く
引数がある関数を作る場合は、丸括弧 () の中に引数として欲しいデータを指定します。そして、処理文の中で、受け取った引数に対する処理を書きます。
例えば、次のコードをご覧ください。引数nameを指定し、そのnameを定義文の中で処理しています。
def greeting(name):
print(name+'さん、こんにちは。')
定義文の中で、引数nameと文字列を連結していますが、文字列は+演算子では文字列としか連結できません(参照:「Pythonの文字列を連結・結合する方法」)。そのため、この関数を呼び出す時は、以下のように、引数に必ず文字列を入力します。
そうすると、次のように出力されます。
greeting('太郎')
なお、引数は複数あっても構いません。
次の関数は、第一引数で大人の人数、第二引数で子供の人数を渡すと、自動で合計料金を計算してくれるものです。
def price(adult, kid):
return adult*2000 + kid*800
引数を2つ設定しているので、この関数を呼び出す時は、引数に2つの値を渡します。
amount = price(2, 3)
print(amount)
4.1.1. 位置引数とキーワード引数
さて、作成したprice()関数は、第一引数は大人の人数、第二引数は子供の人数なので、順番が逆になると出力される結果は異なります。
amount = price(3, 2)
print(amount)
このように、引数の位置によって関数の出力結果が異なるものを「位置引数」といいます。
しかし、price()関数のような関数は、位置引数では「大人、子供」の順だったか、「子供、大人」の順だったか、とどっちがどっちだったかを間違えてしまう可能性があります。
そこで、次のような「キーワード引数」という渡し方をすることもできます。
amount = price(kid=3, adult=2)
print(amount)
このようにキーワード引数は、「キー=値」として関数に渡します。
なおキーワード引数を渡す時は、キーはクオテーションマーク(‘)で囲む必要はありません。もし、そうした場合は、次のように構文エラーになります。
price('kid'=3, 'adult'=2) # キーをクオテーション(')で囲むとエラー
しかし、値が文字列の場合は、値はクオテーションマーク(‘)で囲む必要があります。
この位置引数とキーワード引数は、どちらもよく使うので覚えておきましょう。
4.2. 初期値のある引数がある関数を書く
関数の引数には初期値を設定しておくことも可能です。
以下のコードをご覧ください。
この関数は、服のサイズと個数を渡すと料金を計算してくれます。第二引数のnumには初期値で5を指定しています。なお、変数unit_priceは辞書型オブジェクトです。「Pythonの辞書(dict)の基本的操作の全て」で詳しく解説しています。
def calc(size, num=5):
unit_price = {'S':120, 'M':150, 'L':180}
price = unit_price[size] * num
print(f'{size}が{num}着で合計{price}円です。')
このcalc()関数は、第二引数には初期値5が設定されているので、第一引数でサイズだけ渡すと、個数は5個として計算されます。
calc('L')
第二引数を指定すると、指定した個数で計算されます。
calc('S', 10)
4.3. 可変長の引数のある関数を書く
4.3.1. 可変長の引数
ここまで解説した引数を複数個設定する関数は、必ず、定められた個数の引数を渡さなければエラーになってしまいます。
そのため、それらを「必須引数」と言います。
しかし、時には、引数の個数を固定しない関数が必要な場合があります。そのような場合は、引数の個数を固定しない関数を書くことができます。これを「可変長の引数」といいます。
可変長の引数のある関数を書くには、次のように、引数に *args を入力します。
def 関数名(*args):
関数定義(処理文)
*argsに書いた引数は、タプル型で関数に渡されます。タプルについては「Pythonのタプルの基本的操作まとめ」でご確認ください。
まずは、これを使って、ごく簡単な関数を作ってみます。引数に入力した要素をそのまま出力するものです。どちらも関数に渡した引数はタプル型で出力されていることをご確認ください。また、def文の丸括弧 () の中には *args と書きますが、その下の定義文の中では argsと書くという点も覚えておきましょう。
def station(*args):
print(args)
station() # 引数が0個
station('横浜', '名古屋', '京都') # 引数が3個
可変長の引数と、必須引数は組み合わせることができます。次のコードをご覧ください。
def route(start, goal, *args):
route = [start]
route += list(args)
route += [goal]
route_final = '→'.join(route)
print(route_final)
この関数に、それぞれ引数を渡すと、次のように出力します。
route('東京', '新大阪', '横浜', '名古屋', '京都')
この関数では第一引数のstartと第二引数のgoalが必須引数です。これらは渡さなければ必ずエラーになります。第三引数以降は、可変長の引数なので、入力しなかったからといってエラーになることはありません。
ちなみに関数定義文についても解説しておきます。
まず、必須引数が受け取る値の型は引数に入力した値の型です。この関数では、第一引数も第二引数も文字列を受け取っています。
一方で、可変長の引数は引数に渡された値をタプル型で受け取ります。
つまり、関数が、文字列とタプルという2つの異なる型のオブジェクトを受け取り、そのままでは連結できないので、この関数では、どちらもリスト型に変換しています。
なお、関数定義文の中で使っているjoin()メソッドについては「Pythonの文字列を連結・結合する方法」で解説しています。
4.3.2. 可変長のキーワード引数
**kwargs とすると、その関数は引数を「キーワード引数」で受け取ります。**kwargsに書いた引数は、辞書型で関数に渡されます。
なお、辞書に関しては「Pythonの辞書(dict)の基本的操作の全て」でご確認ください。
さて、キーワード引数を持つ関数を作るには、次のように書きます。
def 関数名(**kwargs):
関数定義(処理文)
それでは、これを使って簡単な関数を作って試してみましょう。
def文の丸括弧 () の中には **kwargs と書きますが、その下の定義文の中では kwargsと書きます。
関数に可変長のキーワード引数を渡す時は、「キー=値」というようにキーと値を=でつないだものを1つの要素として、カンマ区切りで渡します。この時キーは文字
そうすると、引数に渡した要素が辞書型で表示されます。
def fruits(**kwargs):
print(kwargs)
fruits(apple=1, orange=2, banana=3)
なお、このキーワード引数の渡し方はdict()関数で辞書を作成する時と全く同じです(参考:「Pythonの辞書(dict)の作成方法まとめ」)。
可変長のキーワード引数と必須引数も組み合わせることができます。次の関数をご覧ください。第一引数と、第二引数が必須引数で、以降が可変長のキーワード引数です。
def profile(name, gender, **kwargs):
data = {'名前':name, '性別':gender}
data.update(kwargs)
print(data)
profile('山田', '男', 身長='178cm', 体重='70kg')
5. 関数オブジェクトについて
実は、関数は、数値や文字列と同じように、変数に代入することができます。
そして、値に関数を代入した変数のことを「関数オブジェクト」といいます(オブジェクトについては、「Python のオブジェクトとは」で簡単に解説しています)。
関数オブジェクトがあるということは、
- 関数も変数に代入したり、
- 関数自体を、別の関数の引数として渡したり、
ということができるということです。この点についても触れておきましょう。
5.1. 変数に関数を代入する(関数オブジェクトを作る)
まず、関数も変数に代入できるということを見ておきましょう。
単純な関数を作ります。
'''例として hello 関数を作ります。'''
def hello():
print("hello")
'''実行してみましょう。'''
hello()
この hello 関数を、変数 greeting に代入します。そうすると、hello 関数を greeting() と書くことで使用することができます。
'''hello 関数を別の変数に代入します。'''
greeting = hello
'''すると、変数 greeting で hello 関数を実行できるようになります。'''
greeting()
5.2. 関数を別の関数の引数として渡す
次に、関数は、別の関数の引数として渡すことができるということを見ておきましょう。これができるようになると、コードでできることの幅が広がりますので、ここで覚えておいて下さいね。
それでは、以下のコードをご覧ください。
'''thank you と表示する関数です。'''
def thanks():
print("thank you")
'''no thank you と表示する関数です。'''
def no_thanks():
print("no thank you")
'''以下のように引数に func と入れると、引数に関数名を渡し、実行することができます。'''
def do(func):
func()
'''それぞれ実行してみましょう。'''
do(thanks)
do(no_thanks)
このコードでは、まず、 thanks 関数と no_thanks 関数という 2 つの関数を作っています。そして、3 つめの do 関数にご注目ください。
do 関数は、
def do(func):
func()
と書いています。
一行目は、do という名前の関数を作り、(func) と書くことによって、do 関数の引数には、他の関数を渡すということを意味します。このように、(func)と書くと、引数に渡す値は別の関数になるので覚えておきましょう。二行目は、func() と書いています。これは、do 関数の引数に渡した関数を実行するという意味です。
それぞれの実行結果をご覧ください。
do(thanks) では thanks 関数が、do(no_thanks) では no thanks 関数が実行されていますね。
Python では、このように、ある関数の引数を、別の関数にすることができます。
5.3. 関数オブジェクトを使うと書けるコードの幅が広がる
このように、関数オブジェクトを知っていると、書けるコードの幅が大きく広がります。関数オブジェクトを知っているということは、具体的には、以下の 2 つができるということを知っているということです。
- 関数も変数に代入することができる。
- 関数自体を、別の関数の引数として渡すことができる。
それでは、この 2 つの特徴を利用したコードを見てみましょう。
まずは、後者の特徴を利用したコードです。以下をご覧ください。
'''関数オブジェクトを利用すると書けるコードの幅が広がります。'''
condition = 1
if condition ==1:
do(thanks)
else:
do(no_thanks)
このコードは、if 文の条件を満たす時は、thanks 関数を実行し、満たさない時は、no_thanks 関数を実行するという内容になっています。 if 文については、「初心者のための Python の if 文(条件分岐)の基礎と使い方まとめ」で復習しておきましょう。
次に、2 つの特徴の両方を活用したコードです。以下をご覧ください。
'''子供の料金を計算する関数 child を作ります。'''
def child(num):
return 400 * num
'''大人の料金を計算する関数 adult を作ります。'''
def adult(num):
return 1000 * num
'''子供か大人かによって料金の計算を振り分けます。'''
def calc(func, num=1): #第一引数に関数を、第二引数に人数を入れます。
price = func(num) #料金は第一引数に渡した関数で計算されます。
return price
'''3つの関数を合わせて以下のようなコードを作ることができます。'''
from random import randint #random モジュールから randint 関数をインポートします。
age = randint(10, 31)
num = randint(1, 6)
if age < 18:
price = calc(child, num) #18才より下の場合、子供料金で計算します。
else:
price = calc(adult, num) #18才以上の場合、大人料金で計算します。
print(f"{age}才のお客様が{num}名で、料金は{price}円です。")
分かりますでしょうか?
このコードでは、calc 関数の第一引数に、先に作った child 関数と、adult 関数を渡しています。そして calc 関数の中で、変数 price に、第一引数に渡した関数を代入しています。そのため、 price を実行すると、計算結果が表示されます。
最後のコードは、この 3 つの関数を使って、18 才以上か、それより下かによって、子供料金と大人料金を振り分ける内容になっています。モジュールのインポートと randint 関数については、「Pythonのモジュールについて抑えておくべき知識とよく使うもの一覧」で解説しています。
難しいと感じた場合は、実際に、上のコードを、一行ずつ自分の手で入力してみてください。それだけで理解が深まっていきます。
'''クロージャを作ってみましょう。'''
def charge(price):
def calc(num): #charge 関数の中で calc 関数を作っています。
return price * num #calc 関数は、price * num を返します。
return calc #charge 関数は calc 関数の戻り値を返します。
'''関数オブジェクトを作ります。'''
child = charge(400) #関数オブジェクト child を作ります。子供料金を400円としています。
adult = charge(1000) #関数オブジェクト adult を作ります。大人料金を1000円としています。
'''関数オブジェクトを実行してみましょう。'''
print(child(3))
print(adult(4))
6. 関数の中に関数を作る(クロージャ、 関数閉包)
次に、クロージャ(関数閉包)について見てみましょう。クロージャとは、関数の中に、関数を定義するものです。
次のコードをご覧ください。
'''クロージャを作ってみましょう。'''
def charge(price):
def calc(num): #charge 関数の中で calc 関数を作っています。
return price * num #calc 関数は、price * num を返します。
return calc #charge 関数は calc 関数の戻り値を返します。
'''関数オブジェクトを作ります。'''
child = charge(400) #関数オブジェクト child を作ります。子供料金を400円としています。
adult = charge(1000) #関数オブジェクト adult を作ります。大人料金を1000円としています。
'''関数オブジェクトを実行してみましょう。'''
print(child(3))
print(adult(4))
解説しますね。
まず、charge 関数は、その中に書いた calc 関数の結果を返す関数です。そして、charge 関数の引数は price = 価格を渡します。しかし、それだけでは、この関数はまだ機能しません。次のように、返されます。
charge(400)
この関数が機能するには、料金以外に、calc 関数に対して、 num = 人数を引数として渡す必要があります。そこで、まず次のように書きます。
child = charge(400)
adult = charge(1000)
これによって、 charge 関数を、それぞれ、変数 child と adult に代入しています。その際に、同時に charge 関数の引数を渡しています。これで、関数オブジェクトの child と adult が作られました。それぞれ、price = 価格は既に定義されているので、後は、num = 人数を引数に渡すと、処理を実行することができます。
それが、次のコードですね。
child(3)
adult(4)
このように、クロージャを実行する時は、まず外側の関数を、別の変数に代入します。その際に、外側の関数の引数を渡します。それによって、関数オブジェクトが作られます。そして、新しく作った関数オブジェクトを実行する時に、内側の関数の引数を渡します。
最初は分かりにくいかもしれません。そのような時は、ご自身の手で、一行ずつ写経してみてください。コードの写経は、手っ取り早く内容を理解するための最も効率的な方法です。
7. 比較関数を作る
ここでは、関数の別の使い方を解説します。「Pythonのリストをソートする方法まとめ」で解説している sort メソッドや sorted 関数を思い出してください。
これらでは、要素が長い順にソートする時は、次のように書いていました。
'''sort メソッド'''
list.sort(key=len)
'''sorted 関数'''
sorted(list, key = len)
引数に “key = len”と渡されていますね。”len” は文字列やリストの長さを返す関数です(「Pythonのリストの長さ(要素の数)を確認する方法まとめ」)。他にも、引数に “key=str.lower()”と渡せば、大文字小文字の区別なくソートすることができます。
これは、ある関数を、「比較関数」として使えるということを意味します。
文字で細かい説明をするより、実例を見た方が理解が早いと思いますので、早速、以下をご覧ください。
def 文で、size 関数を作っています。
'''def 文で関数 size を作ります。'''
def size(item): # item はオブジェクトの要素を引数とする時に使います。
sizeList = ["XS", "S", "M", "L", "XL"]
pos = sizeList.index(item)
return pos #sizeList の要素のインデックス番号を返します。
size 関数は、ブロック内で定義している XS, S, M, L, XL が引数として渡された場合のみ、その位置を返す関数です。コード内で使っている index メソッドについては、「Pythonのリストを検索する方法まとめ」をご覧ください。
それでは、size 関数を確認してみましょう。
'''size 関数はリスト内の要素の位置を返す関数です。'''
print(size("M"))
print(size("XL"))
それぞれ、該当するインデックス番号が返されていますね。なお、def 文のブロック内で定義している XS, S, M, L, XL 以外を引数に渡すとエラーになります。これを sort メソッドや sorted 関数のキーとして使うと、指定の順番でソートすることができます。
以下をご覧ください。
'''size関数で指定した要素がランダムに並んでいるリストを作ります。'''
data = ["S", "L", "XL", "M", "L", "XS", "XL", "L", "S"]
'''key を size 関数にして sort メソッドを実行します。'''
data.sort(key=size)
print(data)
'''同様に sorted 関数も実行してみます。'''
data_sorted = sorted(data, key=size)
print(data_sorted)
このように size 関数の def ブロックの、 sizeList に書いた順番でソートされます。このように、def 文によって、比較関数を作ることもできるので、覚えておきましょう。
なお、通常、このような比較関数を作る時は、lambda式を使う方がより良い書き方です。「Pythonのlambda(ラムダ)式の書き方と使い方まとめ」もあわせてご確認ください。
8. まとめ
いかがだったでしょうか?
def構文を使った関数の作成は、Pythonでは非常によく行います。最後に、ポイントをまとめておきますね。
def構文を使った関数を使いこなせるようになるべき理由
- 同じコードを何度も書く手間を省略できる。
- 様々な作業の手間を省略できる。
- 美しく簡潔なコードを書くための基礎となる。
def構文を使った関数の作り方
- 構文はdefから始める。
- 関数名は一目見て意味を理解できるものにする。
- 引数がある関数ない関数を作ることができる。
- 関数機能を定義するステートメントの前に半角空白4個分のインデントを入れる。
- 戻り値のある/なしは関数実行時に返したい値の複雑さで決める。
- コメントアウトを使って、関数の説明ドキュメントを加える癖をつける。
ぜひマスターしておきましょう!
コメント
コメント一覧 (1件)
本当に分かり易い解説に感謝しております。
これからも宜しくお願い致します。