関数呼び出しでタプルアンパック式の後に名前付き引数のみを許可するのはなぜですか?
>>> def f(a,b,c):
... print a, b, c
...
>>> f(*(1,2),3)
File "<stdin>", line 1
SyntaxError: only named arguments may follow *expression
それは単なる審美的な選択ですか、それともこれを許可すると曖昧さが生じる場合がありますか?
関数呼び出しでタプルアンパック式の後に名前付き引数のみを許可するのはなぜですか?
>>> def f(a,b,c):
... print a, b, c
...
>>> f(*(1,2),3)
File "<stdin>", line 1
SyntaxError: only named arguments may follow *expression
それは単なる審美的な選択ですか、それともこれを許可すると曖昧さが生じる場合がありますか?
人々が「自然に」これを好まない理由は、補間されたシリーズの長さに応じて、後の引数の意味があいまいになるためだと確信しています。
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 で導入されたようです。ここでは以前の構文について説明しますが、その動作に関する規則はかなり複雑でした。余分な引数の追加は、 * マーカーがなかったときに最後に「しなければならなかった」ため、単純に持ち越されたようです。最後に、電子メールではなく、引数リストに関する長い議論についての言及があります。]
関数定義のスター表記との一貫性のためだと思います。これは、結局のところ、関数呼び出しのスター表記のモデルです。
次の定義では、パラメーター*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))
いくつかの観察:
f(c=3, *(1, 2))
します(この例ではまだ出力されます1 2 3
)。これは、(i)関数呼び出しのほとんどの引数が定位置であり、(ii)プログラミング言語のセマンティクスが明確である必要があるため(つまり、定位置引数とキーワード引数を処理する順序でどちらかの方法を選択する必要があるため)、理にかなっています。 )。f(*(1, 2), 3)
、それはそうであるべきでしょうf(1, 2, 3)
かf(3, 1, 2)
、そしてなぜどちらかの選択が他方よりも理にかなっているのでしょうか?def g(a, b, *c, d)
。d
キーワード引数として以外に値を提供する方法はありません(位置引数はによって「取得」されc
ます)。まず第一に、ラッパー関数を使用して非常によく似たインターフェースを自分で提供するのは簡単です:
def applylast(func, arglist, *literalargs):
return func(*(literalargs + arglist))
applylast(f, (1, 2), 3) # equivalent to f(3, 1, 2)
次に、構文をネイティブにサポートするようにインタープリターを拡張すると、パフォーマンスが非常に重要な関数アプリケーションのアクティビティにオーバーヘッドが追加される可能性があります。コンパイルされたコードにいくつかの追加の命令しか必要としない場合でも、これらのルーチンの使用頻度が高いため、ユーザー ライブラリに頻繁に簡単に収容されるわけではない機能と引き換えに、容認できないほどのパフォーマンス ペナルティが発生する可能性があります。
Python 3のキーワードのみのパラメータがある場合、
def f(*a, b=1):
...
次に、にf(*(1, 2), 3)
設定a
するようなものを期待するかもしれませんが、もちろん、必要な構文が許可されていても、キーワードのみのパラメーターはキーワードのみである必要があるため、許可されません。許可された場合は、デフォルトに設定してそのままにしておく必要があると思います。したがって、Pythonが大いに避けようとしていることである、期待されるもののあいまいさほど、構文のあいまいさではない可能性があります。(1 , 2)
b
3
f(*(1, 2), b=3)
a
(1, 2, 3)
b
1
順序を変更します。
def f(c,a,b):
print(a,b,c)
f(3,*(1,2))