28

関数呼び出しでタプルアンパック式の後に名前付き引数のみを許可するのはなぜですか?

>>> def f(a,b,c):
...     print a, b, c
... 
>>> f(*(1,2),3)
  File "<stdin>", line 1
SyntaxError: only named arguments may follow *expression

それは単なる審美的な選択ですか、それともこれを許可すると曖昧さが生じる場合がありますか?

4

6 に答える 6

31

人々が「自然に」これを好まない理由は、補間されたシリーズの長さに応じて、後の引数の意味があいまいになるためだと確信しています。

def dangerbaby(a, b, *c):
    hug(a)
    kill(b) 

>>> dangerbaby('puppy', 'bug')
killed bug
>>> cuddles = ['puppy']
>>> dangerbaby(*cuddles, 'bug')
killed bug
>>> cuddles.append('kitten')
>>> dangerbaby(*cuddles, 'bug')
killed kitten

最後の 2 つの呼び出しを見ただけでは、dangerbabyどちらが期待どおりに動作し、どちらが子猫のふわふわした子を殺しているかわかりません。

もちろん、この不確実性の一部は、最後に補間するときにも存在します。しかし、混乱は補間されたシーケンスに限定されます - のような他の引数には影響しませんbug

[私は何か公式を見つけることができるかどうかを確認するために簡単な検索を行いました. varag の * プレフィックスは python 0.9.8 で導入されたようです。ここでは以前の構文について説明しますが、その動作に関する規則はかなり複雑でした。余分な引数の追加は、 * マーカーがなかったときに最後に「しなければならなかった」ため、単純に持ち越されたようです。最後に、電子メールではなく、引数リストに関する長い議論についての言及があります。]

于 2012-05-23T22:10:55.393 に答える
6

関数定義のスター表記との一貫性のためだと思います。これは、結局のところ、関数呼び出しのスター表記のモデルです。

次の定義では、パラメーター*cは後続のすべての非キーワード引数を丸呑みするため、fが呼び出されたときに値を渡す唯一の方法はd、キーワード引数として渡すことです。

def f(a, b, *c, d=1):
    print "slurped", len(c)

(このような「キーワードのみのパラメーター」は Python 3 でのみサポートされています。Python 2 では、スター付きの引数の後に値を割り当てる方法ないため、上記は違法です。)

したがって、関数定義では、スター付きの引数はすべての通常の位置引数の後に続く必要があります。あなたが観察したことは、同じルールが関数呼び出しに拡張されたことです。このように、スター構文は関数宣言と関数呼び出しで一貫しています。

もう 1 つの類似点は、関数呼び出しで 1 つの (単一の) スター付き引数しか持てないことです。以下は違法ですが、許可されていることは容易に想像できます。

f(*(1,2), *(3,4))
于 2012-06-08T23:22:47.907 に答える
1

いくつかの観察:

  1. Pythonは、キーワード引数の前に位置引数を処理f(c=3, *(1, 2))します(この例ではまだ出力されます1 2 3)。これは、(i)関数呼び出しのほとんどの引数が定位置であり、(ii)プログラミング言語のセマンティクスが明確である必要があるため(つまり、定位置引数とキーワード引数を処理する順序でどちらかの方法を選択する必要があるため)、理にかなっています。 )。
  2. 関数呼び出しで右側に位置引数がある場合、それが何を意味するかを定義するのは困難です。と呼ぶ場合f(*(1, 2), 3)、それはそうであるべきでしょうf(1, 2, 3)f(3, 1, 2)、そしてなぜどちらかの選択が他方よりも理にかなっているのでしょうか?
  3. 公式の説明として、PEP 3102は、関数定義がどのように機能するかについて多くの洞察を提供します。関数定義のスター(*)は、位置引数の終わりを示します(セクション仕様)。理由を確認するには、次のことを検討してくださいdef g(a, b, *c, d)dキーワード引数として以外に値を提供する方法はありません(位置引数はによって「取得」されcます)。
  4. これが何を意味するかを理解することが重要です。星は位置引数の終わりを示すため、すべての位置引数はその位置またはその左側にある必要があります。
于 2012-06-12T19:16:01.697 に答える
1

まず第一に、ラッパー関数を使用して非常によく似たインターフェースを自分で提供するのは簡単です:

def applylast(func, arglist, *literalargs):
  return func(*(literalargs + arglist))

applylast(f, (1, 2), 3)  # equivalent to f(3, 1, 2)

次に、構文をネイティブにサポートするようにインタープリターを拡張すると、パフォーマンスが非常に重要な関数アプリケーションのアクティビティにオーバーヘッドが追加される可能性があります。コンパイルされたコードにいくつかの追加の命令しか必要としない場合でも、これらのルーチンの使用頻度が高いため、ユーザー ライブラリに頻繁に簡単に収容されるわけではない機能と引き換えに、容認できないほどのパフォーマンス ペナルティが発生する可能性があります。

于 2012-05-23T19:20:16.287 に答える
0

Python 3のキーワードのみのパラメータがある場合、

def f(*a, b=1):
    ...

次に、にf(*(1, 2), 3)設定aするようなものを期待するかもしれませんが、もちろん、必要な構文が許可されていても、キーワードのみのパラメーターはキーワードのみである必要があるため、許可されません。許可された場合は、デフォルトに設定してそのままにしておく必要があると思います。したがって、Pythonが大いに避けようとしていることである、期待されるもののあいまいさほど、構文のあいまいさではない可能性があります。(1 , 2)b3f(*(1, 2), b=3)a(1, 2, 3)b1

于 2012-06-13T20:51:58.833 に答える
0

順序を変更します。

def f(c,a,b):
    print(a,b,c)
f(3,*(1,2))
于 2012-05-23T18:01:22.730 に答える