2017-09-10 5 views
1

例は、私がに `* args`を指定すると、キーワードの引数は位置になりますか?

def myfunc(a=1, *args): 
    print(a) 
    # *args have some other functionality, for example passed to a class instance, or whatever 

関数を定義すると言う問題がある:myfuncを呼び出すときに、私はキーワード引数aを渡し、その後、最初の*argsの予想位置引数を渡すことはできません。期待*argsだけの数が含まれている場合たとえば、私は複数の割り当てを受けたために述べて、エラーを発生させるでしょう

myfunc(3, a=4) 

これを行うことはできません。私は明らかにmyfunc(a=4, b=5, 3)をすることはできません。私は、*argsで、だから今それはそうaが実際の位置となり、キーワード引数を

myfunc(4, 3) 

を行うことができますか?

ここでの例は関数ですが、クラスにも適用されます。 __init__(a=3, *args)のサブクラスが、いくつかのキーワード引数aと他の99個の必須引数をsuperに渡すことを望んでいるとします。私は、サブクラスで99の引数定義を繰り返すのではなく、*argsを実行してsuperに渡します。しかしキーワード引数aは現在位置付けられています。

このクラスとサブクラスの例は、私のコーディングで私の本当の問題です。私は、オプションのキーワード引数aを持つサブクラスを必要とし、そのsuperには99個の必須引数が必要です。私はこの問題の周りに良い、きれいな方法が表示されません。

私はこれに最初に衝突することはできませんが、私は答えを見つけることができませんでした。

+1

私はそれを行う方法はあなたの 'a'を取り除き、' * kwargs'を使うことだと思います。それでも 'a'のデフォルト値が必要な場合は、関数/クラス内でそれを実装する必要があります。 精度として、Python '* args'は「他の引数が来る」と言っているようです。あなたの例では、エラーを発生させる例では、Pythonは最初の引数を知っている名前付き引数( 'a')にマップします。したがって、' a'の値を明示的に設定したい場合、Pythonは既に値それのための。 –

+0

コメントありがとうございます。あなたは** kwargsを意味しましたか?うん、私はこのようなことが起こる理由を理解しています。私はちょうど '* args'でキーワードの引数が位置付けられることに驚いています。実際のコーディングで実際に実行しているのは、クラスとサブクラスの例に似ていますが、その周りに良い方法はありません。 – Alex

+0

@Alex。私はここで何か不足しているかもしれませんが、 '* args'の後に*キーワード引数*で関数を定義するだけではどうですか? – ekhumoro

答えて

2

aは、位置だけではありません。

>>> import inspect 
>>> def myfunc(a=1, *args): 
...  print(a) 
>>> inspect.signature(myfunc).parameters['a'].kind 
<_ParameterKind.POSITIONAL_OR_KEYWORD: 1> 

そして実際、それはキーワード引数として渡すことができます。

>>> myfunc(a=4) 
4 

しかし、あなたはできるだけ早くあなたがしたいと、述べたようにその後の位置引数を指定するには、位置パラメータとして指定する必要があります。それは、すべての引数が名前で渡される必要があり、すべての位置引数またはの最初である必要がありますいずれか

>>> def myfunc2(a, b): 
...  print(a) 
>>> myfunc2(1, a=2) 
TypeError: myfunc2() got multiple values for argument 'a' 

:しかし、それは*argsとは何の関係もありません、それがどれだけ位置引数が仕事です。

しかし、*argsは、位置的な引数のみを収集するため、状況が多少複雑になります。つまり、のいずれかを収集する必要がある場合は、のすべてのパラメータを位置別に渡す必要があります。

しかし、あなたが最初の位置引数として、あなたはそれがキーワード(名前の)引数として渡された場合だけ*args, **kwargsと最初のチェックを受け入れるか、もしできなかった、独自の特殊なケースをロールする場合:

>>> def myfunc3(*args, **kwargs): 
...  if 'a' in kwargs: 
...   a = kwargs.pop('a') 
...  else: 
...   a = args[0] 
...   args = args[1:] 
...  print(a, args, kwargs) 
>>> myfunc3(1, a=2) 
2 (1,) {} 
>>> myfunc3(2, 1) 
2 (1,) {} 

こと期待どおりの結果を得られるはずですが、Pythonsシグネチャモデルのセマンティクスが多少損なわれるため、ドキュメンテーションでどのように引数を渡すべきかを明示する必要があります。

+0

答えをありがとう。しかし、ソリューションはうまくいきますが、あなたが指摘したように、コードにはちょっとしたノイズがあります。私はこの時点でかなり肯定的です。私は、私が望んでいたのは、Pythonプログラミングではよくあることだと思います。基本クラスがあり、オプションのキーワード引数を受け入れるサブクラスを作成し、他の引数をスーパーにバイパスします。しかし、明らかに、少なくともPython 2.7ではそれを行うためのきれいな方法はありません。 – Alex

+1

@Alexはい、それはよくあるケースですが、 '* args'や' ** kwargs'の存在は常に署名を破壊します(例:['inspect.getargspec'](https://docs.python.org /library/inspect.html#inspect.getargspec)または['inspect.signature'](https://docs.python.org/library/inspect.html#inspect.signature)を参照してください)。ですから、Pythonでは、通常は "パラメータをコピー"し、必要のないものを追加または削除したいものを追加します。それはイントロスペクションを維持し、私が答えで述べたような「ハック」を必要としないからです。 – MSeifert

関連する問題