Python学習チャンネル by PyQ

Pythonのオンライン学習プラットフォームPyQのオフィシャルブログです

「(dct, /, **kwargs)の/が表すものとは?」仮引数**kwargsの前に/をつける意味について解説します

f:id:kenken0326:20200721115706p:plain

こんにちは。PyQサポートです。 今回は「**kwargsの前の/」の記述に関する質問です。

質問

最初のコードを実行するとTypeErrorになりますが、模範解答のように**kwargsの前に/を書くとエラーになりません。**kwargsの前に/をつける意味を教えてください。

プログラムの該当箇所

TypeErrorになるコード

def update(dct, **kwargs):
    dct.update(kwargs)

dct = {'year': 2019}
update(dct, year=2020)
print(dct)
try:
    update(dct, dct=2020)
    print(dct)
except TypeError as e:
    print('TypeError:', e)

模範解答のコード

def update(dct, /, **kwargs):
    dct.update(kwargs)

dct = {'year': 2019}
update(dct, year=2020)
print(dct)
try:
    update(dct, dct=2020)
    print(dct)
except TypeError as e:
    print('TypeError:', e)

回答

**kwargsの前の/が表すもの

**kwargsの前に/ をつけることで、位置引数とキーワード引数を明確に分離することができます。これは、「Positional-only parameterss」と呼ばれています。実際のコードでその動きを見てみましょう。

2つのコードの違いは、最初の行だけです。 模範解答のコードに沿って解説します。

1回目のupdateで行われる処理

関数updateでは「kwargsの内容で、辞書dictを更新」しています。 kwargsはキーワード引数で渡された値です。

以下の処理は {'year': 2019} という辞書と、キーワード引数 year=2020update関数に渡しています。

dct = {'year': 2019}
update(dct, year=2020)
print(dct)

このときupdate関数に渡される値は以下のようになります。

  • dct: {'year': 2019}
  • kwargs: {'year': 2020}
def update(dct, /, **kwargs):
    dct.update(kwargs)

dctkwargsyear というキーを持っているので、結果は kwargs の値である 2020 で上書きされます。

2回目のupdateで行われる処理

続く処理も基本的には同じですが、引数が違います。

try:
    update(dct, dct=2020)
    print(dct)
except TypeError as e:
    print('TypeError:', e)

ここでポイントは、 dct=2020 というキーワード引数を渡していることです。

もし def update(dict, /, **kwargs) が、 def update(dict, **kwargs) の場合これはエラーになります。 なぜなら dct という同じ名前の引数が2つ指定されているからです。

しかし、 / をつけることで、「位置引数は位置引数、キーワード引数はキーワード引数」と明確に分離されます。 なので上記の処理はエラーになりません。

まとめ

この問題は / の挙動の違いを確認できます。 ぜひ / を付けてエラーがでないこと、 / を外してエラーがでることを確認してください。

併せて下記のブログの「Positional-only parameterss」の項を読むと、より理解できますよ。

blog.pyq.jp

Copyright ©2017-2020 BeProud Inc. All rights reserved.