0

次のようなデータがあります。

args, kwargs = (('foo', 'bar', 'baz'), {'goo': 1})

そして、オブジェクト内に、これらのデータを引数として必要とする関数があります。これらは、この種の署名を持つメソッドを介して提供されます ( として表されます*args):

callFunctions((*args, **kwargs), ...)

または、その構造でより明示的に:

callFunctions(((args ...), {kwargs ...}), ...)

(この方法で私が期待していることは十分に明確であることを願っています。)

例として、私の 2 つの関数が次のようになっているとします。

def func1(foo, bar):
    print foo, bar

def func2(baz, goo=0):
    print baz, goo

funcs = func1, func2    # for iteration

その背景で、ここに私が抱えている2つの問題があります。

関数ごとに の形式を取得*argsする**kwargs

モジュールを使用して各関数の引数仕様を取得しようとしてきたので、とinspectの 1d データで構造体を「埋める」ことができます。私は次のことを試しました:argskwargs

format = [(spec.args, spec.keywords) for spec in (inspect.getargspec(func) for func in funcs)]

しかし、なぜ がspec.keywords常に私に与えるのか理解できませんNone(一種の愚かなようです)。キーワード引数のデフォルト値は に表示されますがspec.defaultsそれを正しいキーワード引数に関連付ける方法がわかりません。(すべてのキーワード引数が に入れられるのもイライラしますspec.args。)

[(['foo', 'bar'], None), (['baz', 'goo'], None)]

渡される構造体を埋めるcallFunctions

とにかく構造体があると仮定すると、その構造体を元のデータで埋めるのは難しいです*args, **kwargs。次に、次の位置引数と適切なキーワード引数を使用して、次のタプルに移動します。等々。私は次のことを試しました:

argues = []
positional_index = 0
format = ((('foo', 'bar'), {}), (('baz',), {'goo': 0}))
for pair in format:
    however_many = len(pair[0])
    argues.append((tuple(args[positional_index:however_many]), dict({(k, kwargs[k]) for k in pair[1]})))
    positional_index += however_many

しかし、私はこれを取得します:

[(('foo', 'bar'), {}), ((), {'goo': 1})]

なぜ私は取得しないのbazですか?

4

1 に答える 1

1

あなたの質問の最初の部分について:問題は、(Python 2では)「キーワード」引数と「位置」引数自体を完全に区別していないということです。任意の引数値を位置指定またはキーワードで渡すことができます。引数にデフォルト値がない場合は、その引数を指定する必要がありますが、キーワードで渡すことはできます。(Python 3 では、位置指定で渡すことができない真のキーワードのみの引数を持つことができます。)

したがって、引数をデフォルトと一致させる方法は、デフォルトが最後の引数にのみ適用できることを理解することです。したがって、関数が 4 つの引数を受け入れ、2 つの既定の引数値を持つ場合、それらの値は 3 番目と 4 番目の引数用である必要があり、最初の 2 つの引数には既定値がありません。一般的な言い方をすれば、これは 2 つの位置引数と 2 つのキーワード引数を持つ関数です (ただし、前述したように、これは半分正確です。どの引数も常にキーワードで渡すことができるからです)。ドキュメントにあるように、結果のkeywords一部はgetargspecキーワード引数を保持するためのものではありません。キーワードvarargs引数の名前(例: **kwargs) を保持します (存在する場合)。

それらを一致させる方法を確認するには、基本的にこれを行う のソースコードを見てくださいinspect.formatargspec(文字列表現の代わりにリストを作成するように少し変更しました)。

args, varargs, varkw, defaults = inspect.getargspec(func)
result = []
if defaults:
    firstdefault = len(args) - len(defaults)
for i, arg in enumerate(args):
    if defaults and i >= firstdefault:
        result.append((arg, defaults[i - firstdefault]))
    else:
        result.append((arg,))

質問の 2 番目の部分では、問題positional_index:however_manypositional_index:positional_index+however_many. また、dict/set の混乱は、dict 内包ではなく set 内包を使用しているためです。さらに、 への呼び出しは必要ありませんtuple。次のようにします。

for pair in format:
    however_many = len(pair[0])
    argues.append((args[positional_index:positional_index+however_many], {k: kwargs[k] for k in pair[1]}))
    positional_index += however_many

ただし、これに関する 1 つの問題は、同じ名前の異なる値のキーワード引数を異なる関数に渡すことができないことです。def foo(a, b, c=2)andがある場合、 toおよびtodef bar(a, b, c=8)に渡す方法はありません。これは、最初に dict を 1 つしか渡しておらず、キーを 1 つしか持てないためです。{'c': 1}foo{'c': 2}bar'c'

より一般的には、正当な引数セットを実際に処理したい場合、「位置」引数でさえキーワードで渡すことができるため、これほど単純ではありません。のような関数がある場合def foo(a, b, c=3, d=4)、 --- で呼び出すことは合法foo(1, d=8, c=7, b=6)です。キーワード引数を順不同で渡したり、bデフォルト値がなくてもキーワードで値を渡すこともできます。したがって、一般に、デフォルト値のない引数の数に基づいて位置引数を取得することはできません。キーワードによって渡された特定の引数を実際に調べ、キーワードによって渡されなかったものだけを位置的に渡す必要があります。(もちろん、関数はこの種のケースでは機能しないと言うことができます。それは、関数をどの程度一般化するかによって異なります。)

于 2013-07-10T17:13:47.757 に答える