前回の続きから。今回は関数について触れてみます。
4.6. 関数を定義する
プログラミングにおいて、引数を可変として同じ処理をすることが多くあります。
その都度、同じコードを書くのは非効率です。
そこで関数を定義し、必要な時に関数を呼び出して引数を与えることで、効率よくコードを書くことができます。
関数の定義は、以下の表現となります。
## 関数の定義 def <関数名>(<仮引数1>, <仮引数2>, ...): <処理> ## 関数の呼び出し <関数名>(<実引数1>, <実引数2>, ...)
実引数1は仮引数1に、実引数2は仮引数2に、…
見ての通り、実引数は仮引数の順序通りに対応します。これを位置引数と呼びます。
例として、フィボナッチ数列を生成する関数を定義してみます。
この関数は、任意の整数を引数にとり、その引数を上限値として数列を生成します。
■関数の定義
def fib(n): """Print a Fibonacci series up to n.""" a, b = 0, 1 while a < n: print(a, end=' ') a, b = b, a+b print()
■関数の呼び出し
fib(2000)
■出力
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597
三連引用符(”””)で囲われた部分をdocstringといいます。
いろいろな場面でdocstringを書くことがありますが、上記の場合は関数の説明を記載しています。
関数の中で扱われる変数については、グローバル変数・ローカル変数の違い、シンボルテーブルの話がありますが、少しややこしいので別の機会に書ければと思います。
return文について触れておきます。
先ほどのフィボナッチ数列の関数は、print関数によって数列を表示していますが、実際は”None”を返しています。
フィボナッチ数列を表示させる代わりに、return文を使ってリストを値として返すように書き換えます。
■関数の定義
def fib2(n): # return Fibonacci series up to n """Return a list containing the Fibonacci series up to n.""" result = [] a, b = 0, 1 while a < n: result.append(a) # see below a, b = b, a+b return result
“result = []”で空のリストを定義して、”result.append(a)”でリストに要素を追加することができます。
こうしたリストの操作に関しては、5章で解説されます。
■関数の呼び出し
f100 = fib2(100) f100
■出力
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
4.7. 関数定義についてもう少し
関数で大事なのは引数の定義です。
引数の定義方法に3つあり、組み合わせることも可能です。
- デフォルト引数
- キーワード引数
- 任意の引数リスト(これは次回)
4.7.1 デフォルトの引数値
関数を呼び出す場合、引数を必ず指定しなければなりません。
デフォルト引数を定義することで、引数を指定されない場合にデフォルト値で引数を指定することが可能です。
デフォルト引数の指定方法は以下の通りです。
## 関数の定義 def <関数名>(<仮引数1=default1>, <仮引数2=default2>, ...): <処理>
デフォルト引数の便利な使い方の例として、1つ以上の引数をデフォルト引数を指定する形式です。
■ソースコード
def ask_ok(prompt, retries=4, reminder='Please try again!'): while True: ok = input(prompt) if ok in ('y', 'ye', 'yes'): return True if ok in ('n', 'no', 'nop', 'nope'): return False retries = retries - 1 if retries < 0: raise ValueError('invalid user response') print(reminder)
デフォルト引数ではない”prompt”は、必須の引数となります。
■実行例 その1 (必須引数のみを指定する)
ask_ok('Do you really want to quit?')
■出力 その1
Do you really want to quit? > Hello Please try again! Do you really want to quit? > World Please try again! Do you really want to quit? > yes True
■実行例 その2 (一つのオプション引数を与える)
ask_ok('OK to overwrite the file?', 2)
■出力 その2
OK to overwrite the file? > Hello Please try again! OK to overwrite the file? > World Please try again! OK to overwrite the file? > !! --------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-5-366219b14255> in <module>() ----> 1 ask_ok('OK to overwrite the file?', 2) <ipython-input-1-590121386acf> in ask_ok(prompt, retries, reminder) 8 retries = retries - 1 9 if retries < 0: ---> 10 raise ValueError('invalid user response') 11 print(reminder) ValueError: invalid user response
“retries”でリトライ回数を管理しているのですが、上記の例はリトライ回数が満了(retries < 0)したため、raise文でValueErrorが表示されるようになっているので、このErrorは想定動作となります。
■実行例 その3 (すべての引数を与える)
ask_ok('OK to overwrite the file?', 2, 'Come on, only yes or no!')
■出力 その3
OK to overwrite the file? > Hello Come on, only yes or no! OK to overwrite the file? > no False
デフォルト引数は注意点があります。
「デフォルト引数は関数が定義された時点で、1度だけ評価されます。」
ちょっとなに言っているかわからないので、検証してみます。
■ソースコード
i = 5 def f(arg=i): print(arg) i = 6 f()
関数が定義されたとき(3行目)に”arg=i”が評価される場合は、”5″と出力されるはずです。
関数が呼び出されたとき(7行目)に”arg=i”が評価される場合は、”6″と出力されるはずです。
■出力
5
関数が定義されたときに、argのデフォルト値はi=5が採用されているため、定義後にiが書き換えられても、採用されたデフォルト値”5″はそのまま使用されます。
ただし、デフォルト引数がリストや辞書など変更可能なオブジェクトの場合影響を受けます。
■ソースコード
def f(a, L=[]): L.append(a) return L print(f(1)) print(f(2)) print(f(3))
■出力
[1] [1, 2] [1, 2, 3]
関数が定義された時点で、変数”L”に空リストが反映されます。
1回目の呼び出し(f(1))で、変数”L”に要素”1″が追加され、2回目以降は要素が追加された変数”L”が引き継がれます。
もし、変数”L”が引き継がれるのが好ましくないのであれば、関数を呼び出す度に空リストに上書きする必要があります。
具体的には、
■ソースコード
def f(a, L=None): if L is None: L = [] L.append(a) return L print(f(1)) print(f(2)) print(f(3))
■出力
[1] [2] [3]
4.7.2 キーワード引数
関数を呼び出す際、”kwarg=value”という形式で引数を指定することが可能です。
■関数の定義
def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'): print("-- This parrot wouldn't", action, end=' ') print("if you put", voltage, "volts through it.") print("-- Lovely plumage, the", type) print("-- It's", state, "!")
上記の場合、”voltage”は必須引数、デフォルト引数で定義されている”state, action, type”はオプションとなります。
正常動作については、Pythonチュートリアルを参照ください。
逆に、異常動作についてみてみます。(なぜ異常なのかを理解することは、正常動作を理解することに役立ちます。)
■失敗パターン1(引数が足りない)
parrot() --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-6-563b49e6b32c> in <module>() ----> 1 parrot() TypeError: parrot() missing 1 required positional argument: 'voltage'
■失敗パターン2(必須引数が指定されていない)
parrot(voltage=5.0, 'dead') File "<ipython-input-7-01dca0be3920>", line 1 parrot(voltage=5.0, 'dead') ^ SyntaxError: positional argument follows keyword argument
■失敗パターン3(仮引数”voltage”の指定が重複している)
parrot(110, voltage=220) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-8-8fc3e0075ac1> in <module>() ----> 1 parrot(110, voltage=220) TypeError: parrot() got multiple values for argument 'voltage'
■失敗パターン4(仮引数に無いキーワードを指定する)
parrot(actor='John Cleese') --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-9-8d2ac8bc1850> in <module>() ----> 1 parrot(actor='John Cleese') TypeError: parrot() got an unexpected keyword argument 'actor'
関数を呼び出す際、キーワード引数は必ず位置引数の後でなくてはなりません。
一旦ここまで。その3へ。