3-3. 関数

関数について改めて説明します。

参考:

関数の定義

関数は処理(手続きの流れ)をまとめた再利用可能なコードです。関数には以下の特徴があります。

  • 名前を持つ。

  • 手続きの流れを含む。

  • 返値(明示的あるいは非明示的に)を返す。

len()sum() などの組み込み関数は関数の例です。

まず、関数の定義をしてみましょう。関数を定義するには def を用います。

[1]:
#'Hello'を表示する関数greeting
def greeting():
    print('Hello')

関数を定義したら、それを呼び出すことができます。

[2]:
#関数greetingを呼び出し
greeting()
Hello

関数定義の一般形は以下の通りです。

def 関数名(引数):
    関数本体

1行名はヘッダと呼ばれ、関数名はその関数を呼ぶのに使う名前、引数はその関数へ渡す変数の一覧です。 変数がない場合もあります。

関数本体はインデントした上で、処理や手続きの流れを記述します。

引数

関数を定義する際に、ヘッダの括弧の中に関数へ渡す変数の一覧を記述します。 これらの変数は関数のローカル変数となります。 ローカル変数とはプログラムの一部(ここでは関数内)でのみ利用可能な変数です。

[3]:
#引数greeting_localに渡された値を表示する関数greeting
def greeting(greeting_local):
    print(greeting_local)

関数を呼び出す際に引数に値を渡すことで、関数は受け取った値を処理することができます。

[4]:
#関数greetingに文字列'Hello'を渡して呼び出し
greeting('Hello')
Hello

このようにして引数に渡される値のことを、実引数argument)と呼ぶことがあります。 実引数に対して、ここまで説明してきた引数(ローカル変数である引数)は、仮引数parameter)と呼ばれます。 参考:公式FAQ:実引数と仮引数の違いは何ですか?

実引数のことを引数と呼ぶこともありますので、注意してください。

返値

関数は受け取った引数を元に処理を行い、その結果の返値(1-2で説明済み)を返すことができます。

返値は、return で定義します。関数の返値がない場合は、None が返されます。 return が実行されると、関数の処理はそこで終了するため、次に文があっても実行はされません。 また、ループなどの繰り返し処理の途中でも return が実行されると処理は終了します。 関数の処理が最後まで実行され、返値がない場合は最後に return を実行したことと同じになります。

return の後に式がない場合は、None が返されます。 (return が実行されずに関数の最後まで来たときも同様です。) return を式なしで実行することで、関数の処理を途中で抜けることができます。 また、このような関数は、与えられた配列を破壊的に変更するなど、 呼び出した側に何らかの変化を及ぼす際にも用いられます。

[5]:
#引数greeting_localに渡された値を返す関数greeting
def greeting(greeting_local):
    return greeting_local

#関数greetingに文字列'Hello'を渡して呼び出し
greeting('Hello')
[5]:
'Hello'
[6]:
#入力の平均を計算して返す関数average
def average(nums):
    #組み込み関数のsum()とlen()を利用
    return sum(nums)/len(nums)

#関数averageに数字のリストを渡して呼び出し
average([1,3,5,7,9])
[6]:
5.0

関数の返値を変数に代入することもできます。

[7]:
#関数greetingの返値を変数greetに代入
greet = greeting('Hello')
greet
[7]:
'Hello'

複数の引数

関数は任意の数の引数を受け取ることができます。 複数の引数を受け取る場合は、引数をコンマで区切ります。 これらの引数名は重複しないようにしましょう。

[8]:
#3つの引数それぞれに渡された値を表示する関数greeting
def greeting(en, fr, de):
    print(en + ', ' + fr + ', ' + de)

#関数greetingに3つの引数を渡して呼び出し
greeting('Hello', 'Bonjour', 'Guten Tag')
Hello, Bonjour, Guten Tag

関数は異なる型であっても引数として受け取ることができます。

[9]:
#文字列と数値を引数として受け取る関数greeting
def greeting(en, number, name):
    #文字列に数を掛け算すると、文字列を数の回だけ繰り返すことを指定します
    print(en*number+','+name)

#関数greetingに文字列と数値を引数として渡して呼び出し
greeting('Hello',3, 'World')
HelloHelloHello,World

変数とスコープ

関数の引数や関数内で定義される変数はローカル変数のため、それらの変数は関数の外からは参照できません。

[10]:
#引数greeting_localに渡された値を表示する関数greeting
def greeting(greeting_local):
    print(greeting_local)

greeting('Hello')

#ローカル変数(関数greetingの引数)greeting_localを参照
greeting_local
Hello
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
/tmp/ipykernel_4743/415745769.py in <module>
      6
      7 #ローカル変数(関数greetingの引数)greeting_localを参照
----> 8 greeting_local

NameError: name 'greeting_local' is not defined

一方、変数がグローバル変数であれば、それらの変数は関数の外からも中からも参照できます。 グローバル変数とはプログラム全体、どこからでも利用可能な変数です。

[11]:
#グローバル変数greeting_globalの定義
greeting_global = 'Hello'

#グローバル変数greeting_globalの値を表示する関数greeting
def greeting():
    print(greeting_global)

greeting()

#グローバル変数greeting_globalを参照
greeting_global
Hello
[11]:
'Hello'

グローバル変数と同じ名前の変数を関数内で定義すると、 それは通常はグローバル変数とは異なる、関数内のみで利用可能なローカル変数の定義として扱われます。 グローバル変数と同じ名前の引数を用いる場合も同様です。

[12]:
#グローバル変数greeting_globalと同じ名前の変数に値を代入する関数greeting
def greeting():
    greeting_global = 'Bonjour'
    print(greeting_global)

greeting()

#変数greeting_globalを参照
greeting_global
Bonjour
[12]:
'Hello'

しかし、グローバル変数と同名のローカル変数を定義することは、一般に注意が必要です。 何故なら、ローカル変数としての定義を含む関数内では、同名のグローバル変数を参照できないからです。 たとえば、次のコードは、HelloBonjour が順に印字することを期待するかもしれませんが、

[13]:
def greeting():
    print(greeting_global) # 最初の参照
    greeting_global = 'Bonjour' # ローカル変数の定義
    print(greeting_global)

greeting()
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
/tmp/ipykernel_4743/1083173024.py in <module>
      4     print(greeting_global)
      5
----> 6 greeting()

/tmp/ipykernel_4743/1083173024.py in greeting()
      1 def greeting():
----> 2     print(greeting_global) # 最初の参照
      3     greeting_global = 'Bonjour' # ローカル変数の定義
      4     print(greeting_global)
      5

UnboundLocalError: local variable 'greeting_global' referenced before assignment

最初の greeting_global の参照でエラーになります。 これは、関数内に greeting_global の定義があると、 その関数内どの場所でも greeting_global がローカル変数として参照されるためです。 最初の参照時には、ローカル変数の greeting_global が未定義なので、エラーが生じます。

このように、グローバル変数と同じ名前のローカル変数を使おうとするのは間違いの元です。 グローバル変数と名前が衝突しないように、ローカル変数を定義しましょう。

▲ global 宣言

関数内ではグローバル変数が更新されないのが基本です。 しかし、どうしても関数内でグローバル変数を更新したいときには、 global 宣言を使って更新したいグローバル変数を指定します。

[14]:
#グローバル変数greeting_globalに値を代入する関数greeting
def greeting():
    global greeting_global
    greeting_global = 'Bonjour'
    print(greeting_global)

greeting()

##変数greeting_globalを参照
greeting_global
Bonjour
[14]:
'Bonjour'

global 宣言された変数名は、関数内で常にグローバル変数として参照されます。 これを濫用すると間違いの元になるので、原則として利用しないようにしましょう。

▲キーワード引数

上記の一般的な引数(位置引数とも呼ばれます)では、 事前に定義した引数の順番に従って、関数は引数を受け取る必要があります。

キーワード付き引数(キーワード引数)を使うと、関数は引数の変数名とその値の組みを受け取ることができます。 その際、引数は順不同で関数に渡すことができます。

[15]:
#文字列と数値を引数として受け取る関数greeting
def greeting(en, number, name):
    print(en*number+','+name)

#関数greetingに引数の変数名とその値の組みを渡して呼び出し
greeting(en='Hello', name='Japan', number=2)
HelloHello,Japan

位置引数とキーワード引数を合わせて使う場合は、最初に位置引数を指定する必要があります。

[16]:
#位置引数とキーワード引数を組み合わせた関数greetingの呼び出し
greeting('Hello', name='Japan', number=2)
HelloHello,Japan

▲引数の初期値

関数を呼び出す際に、引数が渡されない場合に、初期値を引数として渡すことができます。

初期値のある引数に値を渡したら、関数はその引数の初期値の代わりに渡された値を受け取ります。

初期値を持つ引数は、位置引数の後に指定する必要があります。

[17]:
#引数の初期値(引数の変数enに対する'Hello')を持つ関数greeting
def greeting(name, en='Hello'):
    print(en+', '+name)

#引数の初期値を持つ関数greetingの呼び出し
greeting('World')
Hello, World

▲可変長引数

仮引数の前に * を付けて関数を定義すると、複数の引数をタプルとして受け取ることができます。 呼び出す側は、引数の個数を変えることができます。 一般に、個数が可変の引数は可変長引数と呼ばれます。

[18]:
#可変長の引数を受け取り、それらを表示する関数greeting
def greeting(*args):
    print(args)

#可変長の引数を受け取る関数greetingに複数の引数を渡して呼び出し
greeting('Hello','Bonjour','Guten Tag')
('Hello', 'Bonjour', 'Guten Tag')

リストやタプルの要素を可変長引数として関数に渡す場合は、* をリストやタプルの前につけて渡します。

[19]:
#リスト型オブジェクトgreeting_listを関数greetingに渡して呼び出し
greeting_list = ['Hello','Bonjour','Guten Tag']
greeting(*greeting_list)
('Hello', 'Bonjour', 'Guten Tag')

▲辞書型の可変長引数

仮引数の前に ** を付けて関数を定義すると、複数のキーワード引数を辞書として受け取ることができます。辞書として受け取られる引数は、辞書型の可変長引数と呼ばれます。

[20]:
#可変長のキーワード引数を受け取り、それらを表示する関数greeting
def greeting(**kwargs):
    print(kwargs)

#可変長のキーワード引数を受け取る関数greetingに複数の引数を渡して呼び出し
greeting(en='Hello', fr='Bonjour', de='Guten Tag')
{'en': 'Hello', 'fr': 'Bonjour', 'de': 'Guten Tag'}

辞書の各キーと値を複数のキーワード引数として関数に渡す場合は、 ** をその辞書の前につけて渡します。

[21]:
#辞書型オブジェクトgreeting_dictを関数greetingに渡して呼び出し
greeting_dict = {'en': 'Hello', 'fr': 'Bonjour', 'de': 'Guten Tag'}
greeting(**greeting_dict)
{'en': 'Hello', 'fr': 'Bonjour', 'de': 'Guten Tag'}

▲引数の順番

位置引数、初期値を持つ引数、可変長引数、辞書型の可変長引数は、同時に指定することができますが、 その際、これらの順番で指定する必要があります。

def 関数名(位置引数, 初期値を持つ引数, 可変長引数, 辞書型の可変長引数)
[22]:
#位置引数、初期値を持つ引数、可変長引数、辞書型の可変長引数
#それぞれを引数として受け取り、それらを表示する関数greeting
def greeting(greet, en='Hello', *args, **kwargs):
    print(greet)
    print(en)
    print(args)
    print(kwargs)

#可変長引数へ渡すリスト
greeting_list = ['Bonjour']

#辞書型の可変長引数へ渡す辞書
greeting_dict = {'de': 'Guten Tag'}

#関数greetingに引数を渡して呼び出し
greeting('Hi', 'Hello', *greeting_list, **greeting_dict)
Hi
Hello
('Bonjour',)
{'de': 'Guten Tag'}

▲変数としての関数

関数は変数でもあります。既存の変数と同じ名前の関数を定義すると、 元の変数はその新たな関数を参照するものとして変更されます。 一方、既存の関数と同じ名前の変数を定義すると、元の関数名の変数はその新たな変数を参照するものとして変更されます。

[23]:
#グローバル変数greeting_globalの定義と参照
greeting_global = 'Hello'
type(greeting_global)
[23]:
str
[24]:
#グローバル変数greeting_globalと同名の関数の定義
#変数greeting_globalは関数を参照する
def greeting_global():
    print('This is the greeting_global function')

type(greeting_global)
[24]:
function