[python初心者向け]関数の引数のアスタリスク(*)の意味

こんにちは、大阪オフィスの数枝です。最近python (boto3)と戯れる日々が続いております。
まだまだわからないことがたくさんありますが、先日1点学んだのでまとめます。表題の通り、関数の引数に出てくるアスタリスク(*)についてです。

ロギングモジュールのドキュメント(https://docs.python.org/ja/3/library/logging.html#logging.debug)を見ていたのですが、debug関数の引数に書かれている*args **kwargsの意味が最初わかりませんでした。


これをきっかけに調べたのですが、結果、引数の中にアスタリスクが出てくるパターンは3つ、「アスタリスク一つ+文字列」,「アスタリスク二つ+文字列」、「アスタリスクのみ」があることがわかりました。それぞれ説明します。

目次

アスタリスク一つ+文字列

上記loggerの例でいうと*args の部分ですね。

こちらは可変長の引数を取る関数を作りたい場合に使います。 例えば以下のような関数を定義するとします。

def hoge(*args):
    print(args)

これを以下のように呼び出します。

hoge('aaa')
hoge('aaa', 'bbb')
hoge('aaa', 'bbb', 123)

すると、結果は以下のようになります。

('aaa',)
('aaa', 'bbb')
('aaa', 'bbb', 123)

与えられた可変長の各引数が、関数内ではargsというタプル型変数の各要素となっています。

タプル型変数ですので、forでループもできます。

def arg_loop(*args):
    for arg in args:
        print(arg + '!')


arg_loop('クラス', 'メソッド')

'''結果
クラス!
メソッド!
'''

可変長でない引数と併せて使う

可変長引数のに定義されている引数については普通に使うことができます。 一方、後ろに定義されている引数はキーワード引数(引数名=値)の形で呼び出す必要があります。そうでないとその値が可変長引数の一部と判定され、後ろに定義されている引数には値が未代入と判定されます。

def loop2(before, *args, after):
    print(before)
    for arg in args:
        print(arg + '!')
    print(after)


loop2('前', 'クラス', 'メソッド', after='後')  # OK
loop2('前', 'クラス', 'メソッド', '後')  # NG / TypeError: loop2() missing 1 required keyword-only argument: 'after'


※少し話が逸れますが、可変長引数の後ろに定義されている引数にデフォルト値が設定されていれば、代入が必須ではなくなるのでエラーにはなりません。

def loop3(before, *args, after='デフォルト'):
    print(before)
    for arg in args:
        print(arg + '!')
    print(after)


loop3('前', 'クラス', 'メソッド', '後')  

'''結果
前
クラス!
メソッド!
後!
デフォルト
'''

アスタリスク二つ+文字列

上記loggerの例でいうと**kwargsの部分ですね。

こちらはも可変長の引数を取る関数を作りたい場合に使います。前述のアスタリスク一つの場合との違いは、関数内での引数値のまとめられかたです。先ほどはタプル型変数の各要素となっていましたが、こちらはdict(辞書)型の各キー、バリューになります。そのため、引数の指定もkey=valueの形にする必要があります。

例えばこのような関数を定義したとします。

def fuga(**kwargs):
    for key, value in kwargs.items():
        print('key:' + key + ' / value:' + value)

以下は引数がkey=valueの形になっていないためエラーになります。

fuga('aaa')

'''結果
TypeError: fuga() takes 0 positional arguments but 1 was given
'''

正しくは以下のように呼び出します。

fuga(a='aaa', b='bbb', c='ccc')

'''結果
key:a / value:aaa
key:b / value:bbb
key:c / value:ccc
'''

可変長でない引数と併せて使う

可変長引数のに定義するのは問題ありません。

def fuga2(before, **kwargs):
    print(before)
    for key, value in kwargs.items():
        print('key:' + key + ' / value:' + value)


fuga2('前', a='aaa', b='bbb', c='ccc')
fuga2(before='前', a='aaa', b='bbb', c='ccc')

可変長引数の後ろに定義するのはSyntaxError: invalid syntaxになります。

「アスタリスク一つ+文字列」と「アスタリスク二つ+文字列」を併せて使う

できます。(というか最初に紹介したloggerがやってますが)
かなり自由に引数を定義できます。

def various_args(*args, **kwargs):
    for arg in args:
        print(arg + '!')
    for key, value in kwargs.items():
        print('key:' + key + ' / value:' + value)


various_args('クラス', 'メソッド',  a='aaa', b='bbb', c='ccc')

'''結果
クラス!
メソッド!
key:a / value:aaa
key:b / value:bbb
key:c / value:ccc
'''

以下のように引数順をぐちゃぐちゃにしてもよしなに処理してくれるのかなと思って試してみたのですが、エラーになりました。

various_args(a='aaa', 'クラス', 'メソッド',  b='bbb', c='ccc')
'''結果
SyntaxError: positional argument follows keyword argument
'''

アスタリスクのみ

こちらは前述の二つとは少し毛色が異なります。

def single_asterisk(arg1, *, arg2):
    print(arg1 + ' and ' + arg2)

上記のような関数を定義した場合、引数欄にある*は、*以降に定義されている引数、つまりこの場合だとarg2 をキーワード引数(引数名=値)として受け取ることを強制します。 *自体は引数にはなりません。

例えば以下のように呼び出します。

single_asterisk('a', arg2='b')

'''結果
a and b
'''

single_asterisk(arg1='a', arg2='b')  # これでもOK

以下はエラーになります。

single_asterisk('a', 'b')

'''結果
TypeError: single_asterisk() takes 1 positional argument but 2 were given
'''

「アスタリスクのみ」の後に「アスタリスク一つ+文字列」を使う

できません。SyntaxError: invalid syntax になります。

まぁそうですよね。*でキーワード引数を強制するのに、「アスタリスク一つ+文字列」はキーワード引数の形ではないですからね。

「アスタリスクのみ」の後に「アスタリスク二つ+文字列」を使う

「アスタリスクのみ」の直後に「アスタリスク二つ+文字列」がくる場合、エラーSyntaxError: named arguments must follow bare *になります。

def single_asterisk_plus(*, **kwargs):
    print(kwargs)

ですが、間に固定長引数が入る場合はOKです。

def single_asterisk_plus2(*, a, **kwargs):
    print(a)
    print(kwargs)


single_asterisk_plus2(a='aaa', b='bbb', c='ccc')

'''結果
aaa
{'b': 'bbb', 'c': 'ccc'}
'''

補足: 実行環境

  • Python 3.7.3