Python基礎講座 16 関数 (Function)
今回の講座では関数について勉強していきましょう。 この講座では便宜のためJupyter Notebookを使います。Notebookの使い方がよく分からない方は、下記のリンクから前回の講座を参考にしてください。
プログラミングにおける関数とは、特定のコードブロックを再利用するために名前を付けて名前空間に保存したオブジェクトのことを言います。もう少し噛み砕いて説明すると、関数とはよく使うコードを毎回また使うたびにコーディングしなくて済むように名前を付けてメモリに保存しておき、再び使いたいときには定義した名前で呼び出して、保存しておいたコードブロックが実行されるようにするプログラムのスキームです。もしプログラミングの基本概念であるD.R.Y. という略語を聞いたことがありますか。D.R.Y. とは"Don’t Repeat Yourself"という文の略語です。つまり同じ行動を繰り返すな、という意味ですね。関数を使うと同じコードの繰り返しが減ってコードが簡潔になり、コーディング時間が短縮されるだけでなく、コードの可読性が高まりデバッグもより簡単になります。
D.R.Y. は"Don’t Repeat Yourself"という文の略語であり、プログラマーなら必ず覚えておくべき重要な概念です。
まず、関数を定義する方法を見ていきましょう。
# def キーワードを使って関数を定義します。
def hello(): # 関数の名前は hello です。
# ここから return キーワードの前までが "function body" です。
"""
この部分は docstring であり、関数についての説明が入ります。
"""
print('Hello!') # 関数が呼び出されると Hello! という文字列を出力します。
return None # return キーワードを使って結果値を返します。上のコードのように関数を定義するためにはdefキーワードを使う必要があります。defキーワードの後ろには関数の名前を定義します。定義した関数の名前の後ろには括弧とコロンを入力します。その下の行から最後の行までが"function body"と呼ばれる部分で、この部分に関数が呼び出されたときに実行されるコードブロックが入ります。function bodyの最上部には関数についての説明を入れることができます。この部分を"docstring"と呼び、次のように__doc__メソッドを呼び出したり、help関数を使ったときに出力される部分です。
# def キーワードを使って関数を定義します。
def hello(): # 関数の名前は hello です。
# ここから return キーワードの前までが "function body" です。
"""
この部分は docstring であり、関数についての説明が入ります。
"""
print('Hello!') # 関数が呼び出されると "Hello!" という文字列を出力します。
return None # return キーワードを使って関数の結果値を返します。
print('[__doc__ メソッドを使う場合]')
print(hello.__doc__)
print('[help 関数を使う場合]')
help(hello)[__doc__ メソッドを使う場合]
この部分は docstring であり、関数についての説明が入ります。
[help 関数を使う場合]
Help on function hello in module __main__:
hello()
この部分は docstring であり、関数についての説明が入ります。あらかじめ定義した関数は、下のコードのように関数名に括弧を付けて呼び出すことができます。
def hello():
print('Hello!')
hello()Hello!関数を実行するためには、関数名の後ろに括弧を必ず付ける必要があるという点をお忘れにならないでください。もし括弧なしで関数の名前だけを入力すると、関数オブジェクトだけが返され、関数は実行されません。
ユーザーが定義した関数をユーザー定義関数 (user defined function) と言い、先ほど実行したhelp関数のように、Pythonをインストールするときに含まれていてユーザーが定義しなくても使える関数を組み込み関数 (built-in function) と言います。Pythonは便利なbuilt-in関数を多く含んでおり、こうした理由からPythonはバッテリーが含まれた言語と呼ばれています。
関数は引数を持つことができます。英語ではparameterと呼ばれ仮引数とも呼ばれる引数は、関数を定義するときに括弧の中に定義され、関数の中でのみ参照可能です。
def hello(name): # 括弧の中に定義される変数を仮引数(parameter)と言います。
print('Hello, {}!'.format(name))
# 関数を呼び出すときに括弧の中に入るのが実引数で、英語では argument と言います。
hello('pink')Hello pink関数の外でname仮引数を参照すると、以下のようにnameオブジェクトが見つからないというNameErrorが発生します。
def hello(name):
print('Hello, {}!'.format(name))
print(name)NameError: name 'name' is not definedもし関数の外でnameという変数を定義し、関数を実行するとどうなるでしょうか。
def hello(name):
print('Hello, {}!'.format(name))
name = 'sanghee'
hello('pink')Hello, pink!そのままpinkという名前が出力されるのが分かります。それでは、関数の外で定義したname変数の値が変わったのでしょうか。確認してみましょう。
def hello(name):
print('Hello, {}!'.format(name))
name = 'sanghee'
hello('pink')
print(name)Hello, pink!
sanghee関数の外で定義された変数の値が変わっていないのが分かります。関数の中に定義される仮引数は、関数を実行すると関数の名前空間に一時的に保存されたあと消えてなくなる変数であり、同じ名前を持っていたとしても、関数の外で定義された変数とはまったく別の空間に存在する、まったく別のオブジェクトであるという点をぜひ覚えておいてください。
逆に、関数の外の変数と同じ名前を持つ新しい変数を関数の中に定義しても、関数の外の変数に影響を及ぼしません。
name = 'sanghee'
def hello(name):
name = 'you are {}'.format(name)
print('Hello, {}!'.format(name))
hello('pink')
print(name)Hello, you are pink!
sangheeなぜこうなるかを理解するためには、名前空間という概念と原理を理解する必要があります。名前空間についての説明は、基礎講座を終えたあと別の講座で詳しく説明していきます。
引数を持つ関数を呼び出すときは、必ず引数とともに呼び出す必要があります。引数なしで実行すると、以下のようにTypeErrorが発生します。
def hello(name):
print('Hello, {}!'.format(name))
hello()TypeError: hello() missing 1 required positional argument: 'name'このようなエラーを防ぐために、仮引数にデフォルト値を渡すこともできます。この場合、引数が何も渡されなければデフォルト値が使われます。
def hello(name='pink'):
print('Hello, {}!'.format(name))
hello()Hello, pink!ただし、デフォルト値があっても引数を渡せば、渡された引数がデフォルト値を上書きします。
def hello(name='pink'):
print('Hello, {}!'.format(name))
hello('sanghee')Hello, sanghee!関数の引数は1つだけでなく、複数定義して複数の引数を受け取ることもできます。2つの数を入力されてその和を求める関数を作ってみましょう。
def add(x, y):
print(x + y)
add(1, 1)2実は関数を使うほとんどの目的は、結果値を出力するというより、関数の中で処理された結果値を別の変数に保存して再利用するために結果値を返すケースのほうが多いです。関数で処理された結果値を返すためにはreturnキーワードを使う必要があります。
def add(x, y):
return x + y
result = add(1, 1)
print(result)2結果値を返すことで、関数の結果値を別の関数の引数として使うこともできます。
def add(x, y):
return x + y
result = add(add(1, 1), add(1, 1))
print(result)4関数は1つの値だけを返すのではなく、複数の値を返すこともできます。1つの単語を引数として受け取り、最初の文字と最後の文字を返す関数を作ってみましょう。
def first_and_last(word):
return word[0], word[-1]
print(first_and_last('letter'))('l', 'r')関数が2つの値を返すと、タプルが返されることが分かります。このような場合、アンパッキング機能を使って返された値を2つの変数に保存することができます。
def first_and_last(word):
return word[0], word[-1]
first, last = first_and_last('letter')
print('最初の文字: {}, 最後の文字: {}'.format(first, last))最初の文字: l, 最後の文字: r次のようなリストがあり、リストのすべてのアイテムの和を求めるプログラムを作るとしましょう。
list_1 = [1, 2, 3]
list_2 = [4, 5, 6]
list_3 = [7, 8, 9]もちろん下のコードのように組み込み関数のsum関数を使えば、とても簡単に解決できます。
list_1 = [1, 2, 3]
list_2 = [4, 5, 6]
list_3 = [7, 8, 9]
print(sum(list_1))
print(sum(list_2))
print(sum(list_3))6
15
24ですが組み込み関数がないと仮定してプログラムを作ると、次のようなコードが作られる可能性があります。
list_1 = [1, 2, 3]
list_2 = [4, 5, 6]
list_3 = [7, 8, 9]
total = 0
for i in list_1:
total += i
print(total)
total = 0
for i in list_2:
total += i
print(total)
total = 0
for i in list_3:
total += i
print(total)6
15
24このコードを見ると、求める結果値は得られていますが、同じパターンのコードが何度も繰り返されているのが分かります。このように同じコードが何度も繰り返されると、プログラムの基本概念であるD.R.Y. の概念に反する良くないコードです。このような場合は、下のコードのように繰り返される部分を関数として定義して再利用する必要があります。
list_1 = [1, 2, 3]
list_2 = [4, 5, 6]
list_3 = [7, 8, 9]
def get_total(a_list):
total = 0
for i in a_list:
total += i
return total
print(get_total(list_1))
print(get_total(list_2))
print(get_total(list_3))6
15
24どうですか。コードがより簡潔になり、後で同じ演算をまたしなければならないときには、get_totalという関数を呼び出すだけで済むということです。このようにコードの繰り返しを減らし、より良いコードを作るためにコードを修正する作業を"refactoring"と言い、プロの開発者たちも数えきれないほどの多くのrefactoring作業を通じて、より良いプログラムを作るために絶え間なく努力しています。皆さんもコーディングが終わったプログラムを再度レビューし、関数を使って繰り返される部分をなくす良い習慣を身に着けてください。
今日の講座はここで終えて、次の講座ではファイルの読み込み、書き込みについて勉強していきましょう。