Pythonの関数定義と、位置引数/キーワード引数/可変長法引数を受け取る方法。ラムダ式についてギュッとまとめた。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
前回の最後には関数定義の基本を簡単にまとめた。今回は関数定義について少し詳しくまとめよう。関数についての詳細は「Python入門」の「Pythonの関数」など関数について触れている回を参照されたい。
前回述べたように、Pythonで関数を定義する際の基本構文は以下のようになる。
def 関数名(パラメーターリスト):
関数本体のコード
return 戻り値
呼び出し側から受け取る値を保存する変数をパラメーターリストに記述し、関数本体のコードではそれらを利用して、何らかの処理を行い、return文で呼び出し側に値を返すのが基本だ。ただし、Pythonでは関数に値(引数、実引数)を渡す際には幾つかのやり方がある。以下ではそれらについてまとめていこう。
関数を呼び出す際に、それに渡す値のことを引数(実引数)と呼ぶ。引数の渡し方にはおおまかに分けて次の2種類がある。
以下に例を示す。
from math import sqrt
def get_distance(x1, y1, x2, y2):
distance = sqrt((x1-x2) ** 2 + (y1-y2) ** 2)
return distance
distance = get_distance(1, 1, 0, 0) # 位置引数
print(distance)
distance = get_distance(x1=2, y1=2, x2=0, y2=0) # キーワード引数
print(distance)
distance = get_distance(3, 3, y2 = 0, x2 = 0) # 位置引数とキーワード引数の混在
print(distance)
この例では位置引数のみ、キーワード引数のみ、位置引数とキーワード引数の混在の3種類方法で関数に引数を渡している。これらは全て問題なく動作する。
ただし、位置引数とキーワード引数を混在させる際には、「キーワード引数を指定するのは位置引数よりも後」という制約があることには注意が必要だ。例えば、以下のコードは例外を発生する。
distance = get_distance(x1 = 1, 1, 2, 2) # 例外
なお、Python 3では以前よりパラメーターリストで「キーワード専用パラメーター」を指定可能だった(キーワード引数形式でしか値を渡せないパラメーター)。これに追加して、Python 3.8以降では「位置専用パラメーター」つまり「位置引数としてのみ値を渡せる」パラメーターを明記できるようになった。パラメーターリストの記述時に、スラッシュ「/」を置くと、それよりも前にあるパラメーターは「位置専用パラメーター」になり、アスタリスク「*」を置くと、それよりも後ろにあるパラメーターには「キーワード専用パラメーター」になる。
以下に例を示す。
def myfunc(a, b, /, c, *, d, e):
print(f'a: {a}, b: {b}, c: {c}, d: {d}, e: {e}')
この場合、スラッシュ「/」よりも前にある2つのパラメーターaとbは位置専用パラメーターだ。一方、アスタリスク「*」よりも後ろにある2つのパラメーターdとeにキーワード専用パラメーターだ。間にあるパラメーターcは位置引数としても、キーワード引数としても値を渡せる。以下に例を示す。
myfunc(0, 1, 2, d=3, e=4) # 問題なし
myfunc(0, 1, d=3, e=4, c=2) # 問題なし
myfunc(a=0, b=1, c=2, d=3, e=4) # 例外
myfunc(0, 1, 2, 3, 4) # 例外
上の例では、最初の2行は例外を発生させることなく呼び出せる。これは最初の2つの位置専用パラメーターは位置引数を使って値を渡し、他のパラメーターにはキーワード引数形式で値を渡しているからだ。その下の2行は位置専用パラメーターにキーワード引数を使って値を渡そうとしたり、キーワード専用パラメーターに位置引数形式で値を渡そうとしたりしているために例外が発生する。
以下はPython 3.8.1上にJupyter Notebook環境を構築して、その上で上記コードを実行した結果だ(3行目と4行目では例外の種類が違うので、環境がある方は試してみよう)。
関数のパラメーターには「デフォルト引数値」を持たせることができる。デフォルト引数値を持つパラメーターは関数呼び出し時に、その値の指定を省略でき、省略した場合にはデフォルト引数値が指定されたものとして扱われる。関数が多くのパラメーターを持ち、それらが一般には既定の値(デフォルト値)を指定すればよいという状況ではデフォルト引数値を持たせると関数を呼び出す側が毎回毎回同じ値を指定する必要がなくなり、便利に使えるようになる。
デフォルト引数値はパラメーターリストの中で「パラメーター名=デフォルト引数値」のようにして指定する。ただし、デフォルト引数値を持つパラメーターは、それを持たないパラメーターよりもリストの中で後ろに置く必要がある。以下に例を示す。
from math import sqrt
def get_distance(x1, y1, x2=0, y2=0):
distance = sqrt((x1-x2) ** 2 + (y1-y2) ** 2)
return distance
これは2点間の位置を計算する関数だが、ある1点と原点との距離を計算することはよくある。そのため、ここでは第3パラメーターと第4パラメーターでは原点を意味する整数値0をデフォルト引数値として指定している。
呼び出し例を以下に示す。最初の例では全ての引数を指定し、次の例ではデフォルト引数値を省略している(実行結果は省略)。
print(get_distance(2, 2, 1, 1)) # 全ての引数を指定
print(get_distance(2, 2)) # デフォルト引数値を持つものについては指定を省略
関数に任意の個数の引数を渡せるようにもできる。これには可変長引数リスト(任意引数リスト)を使用する。既に見た通り、Pythonでは引数渡しの方法に位置引数とキーワード引数の2つの方法があるため、可変長引数についてもこれらに対応したものがある。
前者には、関数呼び出し時に関数に渡された位置引数のうち、パラメーターリストで対応するものを持たないものがタプルの要素として保管される。このパラメーターの名前としては「args」を使うのが一般的だ。同様に後者には、関数呼び出し時に関数に渡されたキーワード引数のうち、パラメーターリストで対応するものを持たないものが辞書の要素として保管される。このパラメーターの名前としては「kwargs」を使うのが一般的だ。
以下に例を示す。
def myfunc2(a, b, *args, **kwargs):
print(f'a: {a}, b: {b}, args: {args}, kwargs: {kwargs}')
この例では、パラメーターaとbは位置引数としてもキーワード引数としても値を渡せる。これら2つがmyfunc2関数を呼び出すのに必須のパラメーターとなる。そして、それ以上の引数が渡されたときには、位置引数はパラメーターargsに、キーワード引数はパラメーターkwargsに渡される。以下に例を示す。
myfunc2(1, 2, 3, x=4)
myfunc2(x=3, y=4, b=2, a=1)
最初の呼び出し例では、引数「1」と「2」はパラメーターaとbに位置引数として渡される。引数「3」はパラメーターargs(タプル)の要素となる。引数「x=4」はパラメーターkwargs(辞書)の要素「{'x': 4}」となる。2つ目の例では、パラメーターaとbにキーワード引数として値「1」と「2」が渡され、他の2つのキーワード引数はいずれもパラメーターkwargsに渡される。
実行結果を以下に示す。
なお、これらはパラメーターリストの末尾に置く。ただし、可変長位置引数を受け取るパラメーターは、パラメーターリスト中で位置引数の最後に置くことができる。その場合、それ以降のパラメーターはキーワード専用引数として扱われる。
以下に例を示す。
def myfunc3(a, b, *args, c, **kwargs):
print(f'a: {a}, b: {b}, args: {args}, c: {c}, kwargs: {kwargs}')
ここでは位置引数を受け取るパラメーターの最後として可変長位置引数を受け取るパラメーターargsを置いている。そのため、パラメーターcにはキーワード引数形式でしか値を渡せないことに注意しよう。パラメーターkwargsはそれ以外のキーワード引数を受け取る。呼び出し例を以下に示す。
myfunc3(1, 2, 3, 4, c=5, d=6, e=7)
myfunc3(1, 2, 3, 4, d=5)
1つ目の呼び出し例では、最初の2つの引数がパラメーターaとbに渡され、残る2つの位置引数はパラメーターargsの要素となり、キーワード引数「c=5」はパラメーターcに、残る2つのキーワード引数はパラメーターkwargsに渡される。
一方、2つ目の呼び出し例では、キーワード引数として値を渡すしかないパラメーターcに値が渡されないため、例外が発生する。
可変長引数を受け取るパラメーターを持たせる際には、それをパラメーターリスト中のどこに置くかは注意しよう。
最後にラムダ式の定義についてもまとめておこう。ラムダ式は、1行で処理が終わる無名の関数を簡単に定義するのに使える。これは、関数の引数として関数を渡す場面などで便利に使える。つまり、関数本体が1行で済むような関数を事前に定義することなく、関数が必要な場所ですぐに関数を定義して利用できる。
ラムダ式の構文を以下に示す。
lambda パラメーターリスト: 式
「パラメーターリスト」にはそのラムダ式が必要とするパラメーターをカンマ区切りで並べる。「式」には、そのラムダ式で実行する処理を単一の式で記述する。書けるのは単一の式のみなので、おのずとラムダ式(が定義する関数)で行える処理は極めて小規模なものになる。
以下に例を示す。
mylist = list(range(5)) # [0, 1, 2, 3, 4]
newlist = list(map(lambda x: x ** 2, mylist))
ここではmap関数の引数としてラムダ式を利用している。このラムダ式「lambda x: x ** 2」では先ほどの「パラメーターリスト」には「x」のみを指定し、「式」の部分には「x ** 2」とそれを2乗する式を書いている。この式の評価結果が、ラムダ式の戻り値となる。つまり、これは以下の関数を定義して、map関数に渡しているのと同じ意味になる。
def square(x):
return x ** 2
mylist = list(range(5))
newlist = list(map(square, mylist))
ラムダ式を使った方が、コードが簡潔になることが分かる。なお、上記のmap関数呼び出しと、その結果(イテレータ)をlist関数に渡してリストを生成する処理自体はリスト内包表記を使うと次のように書ける。
mylist = list(range(5))
newlist = [x ** 2 for x in mylist]
ここではラムダ式の使用例として、map関数を用いたが、実際にはこちらの書き方がよいだろう。
最後にラムダ式を返す関数を定義する例も示しておこう。関数を返す関数の例も並記するので見比べてほしい。
def get_incrementer(x):
return lambda y: x + y
def get_incrementer2(x):
def incrementer(y):
return x + y
return incrementer
one_incrementor = get_incrementer(1)
two_incrementor = get_incrementer2(2)
print(one_incrementor(1))
print(two_incrementor(1))
今回は関数定義についてまとめた。次回は、文字列やリスト、タプルなど、反復可能オブジェクトの操作についてまとめる。
Copyright© Digital Advantage Corp. All Rights Reserved.