N - 1.13.関数 Editorial /

Time Limit: 0 msec / Memory Limit: 0 KB

前のページ | 次のページ

キーポイント

  • 関数を定義するためにはdef 関数名(パラメータ):という記法を用いる
  • 関数の戻り値を返すにはreturn文を用いる
  • 関数外の変数を更新するには global 文を用いる

関数を自作する

前項では、今まで扱ってきた inputlen が関数であったことや、これ以外にも色々な関数があることを学びました。この関数は予め用意されたものだけではなく、自分で動作を定めた関数を作成することも可能です。このように関数を作成することを、「関数を定義する」と呼びます。このように関数を定義するメリットの一つとして、複数の場所で行われる同じ処理をまとめることができるという点があります。

最も単純な関数定義

まず、最も単純な関数定義から見ていきます。以下は、1, 2, 3を 3 行で出力するという処理をする関数の定義です。

def print_numbers():
    print(1)
    print(2)
    print(3)

defは定義の英語であるdefineから来ているキーワードで、関数の定義をすることを表します。それに続く部分(今回の場合はprint_numbers)は関数の名前を表し、変数名と同様に自由な名前が使用できます。その後の (): の部分は後ほど説明しますので、今は読み飛ばすこととします。

2 行目以降は、関数の処理を表す部分です。これは関数が呼ばれる度に処理されます。通常の Python のコードと同じように書くことができますが、インデントをする必要がある点には注意してください。

復習となりますが、関数は関数名の後にカッコをつけることで呼び出すことができます。この場合では、以下のようになります。

print_numbers() # 1, 2, 3が改行されて出力される

なお、関数は定義した後でないと呼び出すことができません。その点には注意してください。たとえば、以下のようなコードは実行時エラーとなります。

print_one() # NameError: name 'print_one' is not defined

def print_one():
  print(1)

引数を取る関数定義

次に、min(1, 2) のように値を渡すことができる関数を定義してみます。以下は、第一引数と第二引数を足した数を出力する関数の定義と、その実行結果です。

def add_and_print(a, b):
    print(a + b)

add_and_print(1, 2) # 3 が出力される

新しく出てきた事項として、一行目の(a, b)という記述があります。これは、関数の後に書かれた一番目の要素(第一引数)をaというパラメータに、二番目の要素(第二引数)をbというパラメータに格納することを意味しています。このパラメータは関数の処理部分で変数のように用いることができます。

よって、この関数をadd_and_print(1, 2)のようにして呼び出した場合、aには1bには2が格納され、その状態でprint(a + b)が実行された結果として3が出力されることになります。

戻り値を持つ関数定義

ここまでの文法では min のような関数を実装することはできません。これは、return 文を用いることで、関数の中から呼び出し側に値を返すことができます。このような関数の「結果」を戻り値とよびます。以下は、return 分を用いた第一引数に 1 を足した値を返す関数の定義です。

def add_one(a):
    return a + 1

return 文の後には、関数の戻り値としたい値を記述します。すると、その値が関数呼び出しの式の値となります。よって、以下のように変数に代入したり、直接関数の引数として渡すことも可能です。

two = add_one(1) # 変数 two は 2 となる
print(two) # 2 が出力される
print(add_one(1) + 1) # 3 が出力される

また、return文は実行された時点で関数の処理を終了するという役割もあります。これについては、後ほど詳述します。

まとめ

まとめると、関数の定義は以下のような文法で行うこととなります。

def 関数名(パラメータ):
    処理

実は、関数のパラメータにはここでは説明しなかった文法がいくつか存在しています。これを用いると、print関数のように引数の数を自由にできたり、sep=''のように名前で指定できたりするパラメータを作成できるようになります。
興味のある方は、Python公式ドキュメント内の用語集 - parameterを参照してください。

関数定義時のテクニック

return 文の特性と複数の return 文を持つ関数

return 文は、同じ関数内の複数の箇所に書くことができます。例えば、以下はabの小さい方を戻り値とする関数の定義です。

def my_min(a, b):
    if a < b:
        return a
    else:
        return b

if文がどのようなものだったかを思い出すと、abより小さいときにreturn aが、そうでないときにreturn bが実行されることになると分かります。

return 文は、実行された時点で関数の処理を終了します。つまり、以下のadd_one関数のreturn a + 1以降に書かれた文が実行されることはありません。

def add_one(a):
    return a + 1
    print("ここには到達しない")
    return a + 2 # この行にも到達しない
    print("もちろんここにも到達しない")

これを用いると、my_minを以下のように書き換えることができます。

def my_min(a, b):
    if a < b:
        return a
    # a < b のケースでは return a が実行されているため、ここには到達しない
    return b

このコードでは、return bが実行されるのはa < bでないときのみです。何故ならば、a < bのときはreturn aが実行されるので、そこで関数の処理が終了するためです。

また、値を返さない関数1でも、関数の処理を早期に終了する目的でreturn文を使用することができます。

def print_a_is_7(a):
    if a == 7:
        print("a is 7")
        return
    # a == 7 のケースでは return が実行されているため、ここには到達しない
    print("a is not 7")

例えば、上の例では a == 7 のときには print("a is not 7") が実行されません。これは、a == 7 のときは return が実行されているので、そこで関数の処理が終了するためです。

関数内の変数

通常のプログラムと同様に、関数内でも変数を用いることができます。しかし、関数内で使用した変数は、基本的には関数の外部で使用することはできません。

以下の関数は、add_one関数を変数を使って書き直したものです。

def add_one(a):
    result = a + 1
    return result

print(add_one(1)) # 2 が表示される
print(result) # NameError: name 'result' is not defined

この関数自体は、想定している通りの動作をします。しかし、関数を呼び出した後にresult変数を参照しようとしても、存在しない変数を使用しようとしたことを示すエラーが発生してしまいます。

変数を使用できる範囲のことをスコープと呼びます。今回の場合、result変数のスコープはadd_one関数内であるということになります。

一方で、関数の外の変数は自由に使用することが可能です。以下は、変数aの値をプリントするprint_a関数を用いた例です。

a = 0
def print_a():
    print(a)

print_a() # 0 が表示される
a = 1
print_a() # 1 が表示される

変数aは関数の外で宣言されていますが、その値をprint_a関数の内部で用いることができていることが分かります。これは、変数aのスコープがグローバル(つまり、コード全体)であるためです。このような変数のことをグローバル変数と呼びます2

一方で、関数内でグローバル変数を変更する場合には注意が必要です。以下は、受け取った変数をaに代入することを意図した関数update_aを用いた例です。

a = 0
def update_a(val):
    a = val

update_a(1)
print(a) # 0 が出力される

この例から分かる通り、単に代入文を書くだけでは関数内ではグローバル変数の値を変更することができません3。グローバル変数に値を代入したい場合は、global文を使用します。

a = 0
def update_a(val):
    global a
    a = val

update_a(1)
print(a) # きちんと 1 が出力される

関数内にてglobal 変数名という形でglobal文を使用すると、関数内で指定した変数を使用した際にグローバル変数として扱うようになります。また、複数の変数を指定したい場合は、global a, b, c のようにカンマで区切ることで指定することができます。

global文が必要な条件については一見複雑そうに見えますが、まとめれば「関数外部でも変数を使用していて、かつ関数内で代入をしている」場合となります。

よく混乱する例として、list等を代入せずに変更するケースがあります。例えば、以下のようなケースではglobal文は必要ありません。

li = []
def update_li(val):
    li.append(val)

update_li(1)
print(a) # [1] と出力される

これは、変数liは変更されてはいるものの代入されていないためです。global文が必要となるケースは、以下のように変数liに代入しているケースです。

li = []
def update_li(val):
    global li
    li = li + [val]

update_li(1)
print(a) # [1] と出力される

nonlocal

ここからは、global文についての補足となります。複雑なプログラムを書かない内は必要となることがないであろう知識なので、読み飛ばしても構いません。

for文の中にfor文を入れることができたのと同様に、Pythonでは関数内で関数を定義することが可能です。この際、以下のように関数内で用いたい外部の変数がグローバル変数でない場合が存在します。

def get_1():
    value = 0
    def set_1():
        # ここで用いている value はグローバル変数ではないので、global は使えない
        value = 1 # Error: local variable 'value' referenced before assignment
    set_1()
    return value

このような場合に使用できる文として、nonlocal文が存在します。これを用いて上のコードを書き直すと、以下の通りとなります。

def get_1():
    value = 0
    def set_1():
        nonlocal value
        value = 1
    set_1()
    return value

問題

リンク先の問題を解いてください。

EX13.三人兄弟へのプレゼント

前のページ | 次のページ


  1. この表現は正確ではありません。Pythonは全ての関数が戻り値を持ち、それはprint()といった関数も例外ではありません。このように一見戻り値を持たないように見える関数は、実はNoneという「何もない」を意味する特別な値を返しています。 

  2. 対して、スコープがグローバルでない変数をローカル変数と呼びます。 

  3. この際、関数内で使用している変数aと関数外で使用している変数aは別の変数になります。これは変数のシャドウイングと呼ばれます。シャドウイングは混乱の種となりやすいため、初学者のうちは行わない方が無難でしょう。本教材でも、シャドウイングをするコードは登場しません。