Python における関数引数の渡し方いろいろ

Python における関数引数の渡し方いろいろ

Python における関数引数の受け渡しは、本質的にはオブジェクトを関数の仮引数名に束縛することです。「代入」によって渡している、と理解してもよいでしょう。この規則は引数渡しの全体的な原則を説明していますが、さらに、関数の引数をどのように定義するのか、そして関数呼び出し時に引数がどのように解釈されるのかを見ていく必要があります。

関数引数の定義には、よく使われる形式が 4 つあります。

  1. F(arg1, arg2, ...)
  2. F(arg2=<value>, arg3=<value>, ...)
  3. F(*args)
  4. F(**kwargs)

1. 通常の位置引数

1 つ目は、もっとも伝統的な方法です。関数はいくつかの引数を定義でき、仮引数は関数名の後ろの丸括弧の中に置き、各引数はカンマで区切ります。

この形式で定義された関数を呼び出すときは、同じ数の実引数を渡さなければなりません。多すぎても少なすぎてもいけません。また、順序も対応している必要があります。つまり、仮引数と実引数は一対一で対応します。仮引数1 = 実引数1仮引数2 = 実引数2、という具合です。

例:

def add_on(x, y):
    return x + y

この関数は次のように呼び出せます。

add_on(1, 2)

これは、仮引数 x の値が 1、仮引数 y の値が 2 になることを意味します。一方、次の 2 つの呼び出し方はいずれも誤りです。

add_on(1, 2, 3)  # 引数が多すぎる
add_on(1)        # 引数が少なすぎる

2. デフォルト引数とキーワード引数

2 つ目は、関数を定義するときに仮引数へデフォルト値を指定する方法です。関数呼び出し時に対応する仮引数へ実引数が渡されなかった場合、その仮引数にはデフォルト値が使われます。

例:

def add_on(x=3, y=5):
    return x + y

呼び出し:

add_on(6, 5)

これは、x の値が 6y の値が 5 になることを意味します。

呼び出し:

add_on(7)

これは、x の値が 7 になり、y にはデフォルト値 5 が使われることを意味します。

ここで 1 つ問題が出てきます。x にはデフォルト値を使わせ、y だけに値を指定したい場合はどうすればよいのでしょうか。そこで使うのが、Python の関数呼び出しにおけるキーワード引数です。

add_on(y=6)

これは、x にはデフォルト値 3 が使われ、y の値が 6 になることを意味します。

キーワード引数では仮引数名を直接指定できるため、引数定義時の順序に従う必要はありません。例:

add_on(y=4, x=6)

このように仮引数名を使って特定の引数へ値を代入する方法は、通常の位置引数だけで定義された関数にも同じように使えます。

注意すべき点として、デフォルト引数にはよく知られた落とし穴があります。リストや辞書などのミュータブルなオブジェクトをデフォルト値にしてはいけません。デフォルト値は関数定義時に一度だけ作成され、その後の呼び出しでも同じオブジェクトが再利用されるためです。

3. 可変長位置引数 *args

前の 2 つの方法では、定義される仮引数の数は固定です。たとえば関数定義時に 5 個の仮引数を定義したなら、呼び出し時に渡せる実引数も最大 5 個までです。

しかし実際のプログラミングでは、ある関数がいくつの引数を受け取るのかを事前に決められないこともあります。3 つ目の方法は、このような場合に対応するためのものです。アスタリスク 1 つと仮引数名を組み合わせて、たとえば *args のように書きます。この関数は任意の数の位置引数を受け取ることができ、0 個でも N 個でも構いません。渡されたこれらの引数は、関数内部ではタプルになります。

例:

def add_on(*args):
    total = 0
    for value in args:
        total += value
    return total

次の呼び出しはいずれも可能です。

add_on()
add_on(2)
add_on(3, 4, 5, 6)

関数内部では、args はそれぞれ次の値になります。

()
(2,)
(3, 4, 5, 6)

4. 可変長キーワード引数 **kwargs

3 つ目の方法と似ていますが、仮引数名の前にアスタリスクを 2 つ付けると、任意の数のキーワード引数を受け取ることを表します。これらの引数は、関数内部では辞書になります。

このような関数を呼び出すときは、必ず key=value の形式で引数を渡します。例:

def add_on(**kwargs):
    total = 0
    if len(kwargs) == 0:
        return 0
    for value in kwargs.values():
        total += value
    return total

呼び出し方は次のようになります。

add_on()
add_on(x=4, y=5, k=6)

このうち add_on(x=4, y=5, k=6) の呼び出しで、関数内部の kwargs は次のようになります。

{'x': 4, 'y': 5, 'k': 6}

Python 2 を使う場合、辞書の走査は kwargs.itervalues() と書くこともできます。Python 3 では kwargs.values() を使うべきです。

引数の組み合わせ

上で述べた 4 種類の引数定義方法は組み合わせて使うことができ、より複雑で柔軟な関数定義を作れます。関数を定義するときは、通常、次の順序に従います。

  1. 通常の位置引数。例:arg
  2. デフォルト引数。例:arg=<value>
  3. 可変長位置引数。例:*args
  4. 可変長キーワード引数。例:**kwargs

つまり、次のようになります。

def func(arg, default_arg=value, *args, **kwargs):
    pass

関数呼び出しの過程で、仮引数に値が割り当てられる大まかな流れは次のとおりです。

  1. まず、通常の位置実引数を順番に対応する仮引数へ割り当てます。
  2. 次に、arg=<value> 形式のキーワード実引数を対応する仮引数へ割り当てます。
  3. 余った位置実引数はタプルになり、アスタリスク 1 つ付きの仮引数へ割り当てられます。
  4. 余ったキーワード実引数は辞書になり、アスタリスク 2 つ付きの仮引数へ割り当てられます。

聞くと複雑に感じますが、実際にはかなり直感的です。次の例を見てください。

def test(x, y=5, *a, **b):
    print(x, y, a, b)

異なる呼び出し方の結果は次のとおりです。

test(1)                  # 1 5 () {}
test(1, 2)               # 1 2 () {}
test(1, 2, 3)            # 1 2 (3,) {}
test(1, 2, 3, 4)         # 1 2 (3, 4) {}

test(x=1)                # 1 5 () {}
test(x=1, y=1)           # 1 1 () {}
test(x=1, y=1, a=1)      # 1 1 () {'a': 1}
test(x=1, y=1, a=1, b=1) # 1 1 () {'a': 1, 'b': 1}

test(1, y=1)             # 1 1 () {}
test(1, 2, y=1)          # TypeError: test() got multiple values for argument 'y'
test(1, 2, 3, 4, a=1)   # 1 2 (3, 4) {'a': 1}

このうち test(1, 2, y=1) がエラーになるのは、位置引数 2 がすでに y に割り当てられているにもかかわらず、後からキーワード引数 y=1 によって再度 y に値を割り当てようとしているためです。そのため Python は、y が複数の値を受け取ったと報告します。

まとめると、通常の位置引数は引数の数が固定された関数に適しています。デフォルト引数はよく使う値に既定の動作を与えるのに適しています。args は不定数の位置引数を受け取るのに適しており、kwargs は不定数のキーワード引数を受け取るのに適しています。実際に関数を書くときは、引数設計をできるだけ明確にし、本当に柔軟な拡張が必要な場合にだけ args**kwargs を使うべきです。

Leave a Reply