[Pythonクイズ]キーワード引数をいちいち書くのはメンドくさい。そんなときにはどうすればよいですか?Pythonステップアップクイズ

辞書に格納されている要素を関数にキーワード引数として渡したい。しかも、キーワードと辞書のキーが一緒! そんなときに「キーワード=辞書[キー]」とか書いていませんか? もっとよい方法ありますよ。

» 2025年07月29日 05時00分 公開
[かわさきしんじDeep Insider編集部]
「Pythonステップアップクイズ」のインデックス

連載目次

func関数の呼び出し方をもっとスッキリさせたい! func関数の呼び出し方をもっとスッキリさせたい!

【問題】

 以下のコードではfunc関数を定義して、それを呼び出している。func関数の2つのパラメーターはどちらもキーワード専用であり、func関数を呼び出す際には以下のようにパラメーター名をキーワードとして付加する必要があるため、書くのが面倒くさい。もっとカンタンに呼び出す方法はないだろうか。ただし、func関数の定義に手を加えてはならないものとする。

def func(*, name, age):
    print(f'{name} is {age} years old')

hpk = {'name': 'hp kawasaki', 'age': 99}
func(name=hpk['name'], age=hpk['age'])

func関数の呼び出し方をもっとスッキリさせたい!


かわさき

 どうもHPかわさきです。

 今回は上で定義しているfunc関数について聞いてみました。デフォルト引数値の指定もないままに、キーワード専用のパラメーターを置くのがよいか悪いかについてです。選択肢は以下の4つ。

  1. クイズ用に考えたモノなんだから、気にするなよ(笑)
  2. キーワード専用パラメーターにデフォルト引数値を指定しないとかサイテー
  3. いい設計じゃないかな?
  4. その他

 3つのLLM+筆者の解答は次の通りです。

四者四様の解答 四者四様の解答

 筆者としてはもちろん「クイズの問題用に考えたんだから気にしないでくれよ」としかいえません。

 Claudeさんは結構辛辣(しんらつ)で「デフォルト値なしのキーワード専用パラメーターは、関数の柔軟性を損なう可能性があり、設計として少し中途半端に感じられます」という意見のようです。

 ChatGPTさんは「デフォルト値がないのは、必須引数として強制したい意図が感じられるため、サイテーではなく、合理的とも受け取れます」と(ただのクイズの問題なのに)よいことのように考えてくれました(テキトーなんだよ、テキトー)。

 Geminiさんは「これがクイズの問題で使う関数の定義」ということを踏まえて、「よくできた例」としています。聞きたかったのはそういうことじゃないんだけどねぇ。

 LLMと筆者による問題文にある関数の定義がいいものかどうかは四者四様の答えになるという意外な展開になりました。個人的には2と3がいい感じに拮抗してくれるとよかったのですが、Geminiさんの俯瞰(ふかん)的な答えが何ともいい味を出してくれていますね。


【答え】

 正解のコード例を以下に示します。

def func(*, name, age):
    print(f'{name} is {age} years old')

hpk = {'name': 'hp kawasaki', 'age': 99}
func(**hpk)

辞書の要素をアンパックして関数に渡すことでシンプルに呼び出せるよ!

 「**hpk」としてhpkの要素(キーと値の組)をアンパックすることで、「func(name='hp kawasaki', age=99)」と呼び出すのと同じ結果が得られます。

辞書の要素をアンパックして関数に渡すことでシンプルに呼び出せるよ! 辞書の要素をアンパックして関数に渡すことでシンプルに呼び出せるよ!

【解説】

 問題文のコードを見てみましょう。この関数はnameとageという2つのパラメーターを持ち、それらは共にキーワード専用となっています(パラメーターリストでは単独の「*」よりも後ろにあるパラメーターは全てキーワード専用になります)。また、デフォルト引数値が指定されていないので、これら2つのパラメーターは呼び出し時に引数を渡すことが必須です。

def func(*, name, age):
    print(f'{name} is {age} years old')

hpk = {'name': 'hp kawasaki', 'age': 99}
func(name=hpk['name'], age=hpk['age'])

func関数を呼び出すにはnameとageの指定が必須

 また、変数hpkは{'name': 'hp kawasaki', 'age': 99}という辞書を参照しています。たまたま(なのか、意図してなのかはさておき)辞書のキーがパラメーター名と一致しています。このようなときに上のコードにあるように「name=hpk['name']」「age=hpk['age']」のようにパラメーター名に続けて、辞書[キー]と書くのはとても面倒くさいことですよね。

 このようなときには、**演算子を辞書の前に前置することで、辞書の要素(キーと値の組)をアンパックして、関数に「キー=値」という形式で渡せます。それが正解例に示したやり方です。

def func(*, name, age):
    print(f'{name} is {age} years old')

hpk = {'name': 'hp kawasaki', 'age': 99}
func(**hpk)

辞書の要素をアンパックして関数に渡す

 「**hpk」とすることで、実質的には「func(name='hp kawasaki', age=99)」と書いたのと同じことになります。ホント? と思うかもしれませんが、ちょっと下のコードを見てください。

list(**hpk)  # TypeError: list() takes no keyword arguments

list関数に「**hpk」を渡すと「list関数にはキーワード引数はない」と怒られる

 このコードを実行すると「list() takes no keyword arguments」というメッセージ付きでTypeError例外が発生します。このことからも「**hpk」により辞書の要素がアンパックされて、キーワード引数として関数に渡されていることが分かるでしょう。

 ただし、これがうまく機能しているのは、キーワード引数として値を与えられるパラメーターと辞書が持つキーが完全に対応しているからであることには注意してください。以下は、余計なキー/値の組を格納している辞書をfunc関数に渡してみた例です。

foo = {'name': 'FOO', 'age': 100, 'info': 'INFO'}
func(**foo)  # TypeError: func() got an unexpected keyword argument 'info'

余計なキー/値の組を含む辞書をfunc関数に渡すと……

 このような場合には、TypeError例外が発生してしまいます(逆に「func(**{'age': 99})」のようにパラメーターとして指定が必要なキーが不足していても例外が発生します)。

 その一方で、関数が「**kwargs」で任意のキーワード引数を受け取れるようにすることは可能です。

def func2(**kwargs):
    name = kwargs.get('name', 'nanasi')
    age = kwargs.get('age', 0)
    print(f'{name} is {age} years old')

hpk = {'name': 'hp kawasaki', 'age': 99}
func2(**hpk)

**kwargsなら任意のキーワード引数を受け取れる

 こっちがいいじゃん! とカンタンにはいえないところもあります。元の関数ではnameやageはパラメーターとして直接使えましたが、こちらではkwargsにキー/値の組として格納されているので、kwargs[キー]やkwargs.get(キー)などの形でアクセスする必要があります(上のコードでもそうしていますね)。

 また、キーが間違っている(あるいは、関数呼び出し時に「キーワード=値」として指定するキーワードが間違っている)場合でも、関数は問題なく、それらを受け取ってしまうことです。

 以下がその例です。

func2(**{'nama': 'hp kawasaki', 'age': 99})  # キーが間違っている

辞書のキーが想定したものになっていない

 上のコードではキーが'name'ではなく'nama'となってしまっています。func2関数の中では「name = kwargs.get('name', 'nanasi')」として'name'キーの値を取得しているので、'name'キーがないこの場合は'nanasi'がデフォルト値として返されます。コードは例外を発生させることなく実行されますが、果たしてこれが意図した通りの振る舞いといえるのでしょうか(まあ、上のコードならgetメソッドではなく、キーを介したアクセスにすることで必要なモノがなければ例外が発生するようにすればよいのでしょうが)。

 こんなことも発生するので、取りあえず「**kwargs」を使って何でも受け取っておけばいいやみたいなやり方には難しい点もあることには留意しておくのがよいでしょう。


かわさき

 「関数にキーワード引数を渡したい、でも、なるべくならスッキリと呼び出したい」というときには「**」演算子による辞書の要素のアンパックが便利ということは頭に入れておくようにしましょう。

 なお、関数の引数については「Python入門」の「関数の引数」を参照してください。「**」に限らず、いろいろな形式の引数について説明しています。


「Pythonステップアップクイズ」のインデックス

Pythonステップアップクイズ

Copyright© Digital Advantage Corp. All Rights Reserved.

アイティメディアからのお知らせ

スポンサーからのお知らせPR

注目のテーマ

4AI by @IT - AIを作り、動かし、守り、生かす
Microsoft & Windows最前線2025
AI for エンジニアリング
ローコード/ノーコード セントラル by @IT - ITエンジニアがビジネスの中心で活躍する組織へ
Cloud Native Central by @IT - スケーラブルな能力を組織に
システム開発ノウハウ 【発注ナビ】PR
あなたにおすすめの記事PR

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。