web-dev-qa-db-ja.com

Pythonおよびargparseを使用した複数の位置引数

Argparseを使用して、作業中のプログラムのコマンドライン引数を解析しようとしています。基本的に、オプションの引数内で広がる複数の位置引数をサポートする必要がありますが、この状況でargparseを機能させることができません。実際のプログラムでは、カスタムアクションを使用しています(位置引数が見つかるたびに名前空間のスナップショットを保存する必要があります)が、発生している問題はappendアクションで再現できます。 :

>>> import argparse
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('-a', action='store_true')
>>> parser.add_argument('-b', action='store_true')
>>> parser.add_argument('input', action='append')
>>> parser.parse_args(['fileone', '-a', 'filetwo', '-b', 'filethree'])
usage: ipython [-h] [-a] [-b] input
ipython: error: unrecognized arguments: filetwo filethree

これにより名前空間(a=True, b=True, input=['fileone', 'filetwo', 'filethree'])が生成されますが、これがどのように行われるかはわかりません。これが可能である場合、ドキュメントまたはGoogleで何らかの方法で何かを言うことはできませんが、可能性は高いですが(おそらく?)見落としました。誰か提案はありますか?

26
Blair

srgergは、位置引数の定義について正しかった。希望する結果を得るには、それらをオプションの引数として受け入れ、必要に応じて結果の名前空間を変更する必要があります。

カスタムアクションを使用できます。

class MyAction(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):

        # Set optional arguments to True or False
        if option_string:
            attr = True if values else False
            setattr(namespace, self.dest, attr)

        # Modify value of "input" in the namespace
        if hasattr(namespace, 'input'):
            current_values = getattr(namespace, 'input')
            try:
                current_values.extend(values)
            except AttributeError:
                current_values = values
            finally:
                setattr(namespace, 'input', current_values)
        else:
            setattr(namespace, 'input', values)

parser = argparse.ArgumentParser()
parser.add_argument('-a', nargs='+', action=MyAction)
parser.add_argument('-b', nargs='+', action=MyAction)
parser.add_argument('input', nargs='+', action=MyAction)

そして、これはあなたが得るものです:

>>> parser.parse_args(['fileone', '-a', 'filetwo', '-b', 'filethree'])
Namespace(a=True, b=True, input=['fileone', 'filetwo', 'filethree'])

または、結果の名前空間を次のように変更できます。

>>> import argparse
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('-a', nargs='+')
>>> parser.add_argument('-b', nargs='+')
>>> parser.add_argument('input', nargs='+')
>>> result = parser.parse_args(['fileone', '-a', 'filetwo', '-b', 'filethree'])

>>> inputs = []
>>> inputs.extend(result.a)
>>> inputs.extend(result.b)
>>> inputs.extend(result.input)

>>> modified = argparse.Namespace(
        a = result.a != [],
        b = result.b != [],
        input = inputs)

そして、これはあなたが得るものです:

>>> modified
Namespace(a=True, b=True, input=['filetwo', 'filethree', 'fileone'])

ただし、どちらの方法でも、コードの可読性と保守性が低下します。プログラムのロジックを変更して、別の方法で行う方が良いでしょう。

10
Wang Dingwei

この方法では、スイッチ(-a-b)を位置引数(つまり、fileone、filetwo、filethree)にインターリーブすることはできません。スイッチは、中間ではなく、位置引数の前後に指定する必要があります。

また、複数の位置引数を持つためには、nargsパラメータをadd_argumentに指定する必要があります。例えば:

parser.add_argument('input', nargs='+')

これは、argparseに1つ以上の位置引数を消費してリストに追加するように指示します。詳細は argparseのドキュメント を参照してください。この行で、コード:

parser.parse_args(['-a', '-b', 'fileone', 'filetwo', 'filethree'])

結果は:

Namespace(a=True, b=True, input=['fileone', 'filetwo', 'filethree'])
18
srgerg

「追加」アクションは、オプションでより意味があります。

parser.add_argument('-i', '--input',action='append')
parser.parse_args(['-i','fileone', '-a', '-i','filetwo', '-b', '-i','filethree'])

オプションを別々の位置にインターリーブすることはできますが( 'input1 -a input2 -b input3')、1つのマルチアイテムポジション内でオプションをインターリーブすることはできません。しかし、これは2ステップの解析で実現できます。

import argparse
parser1 = argparse.ArgumentParser()
parser1.add_argument('-a', action='store_true')
parser1.add_argument('-b', action='store_true')
parser2 = argparse.ArgumentParser()
parser2.add_argument('input', nargs='*')

ns, rest = parser1.parse_known_args(['fileone', '-a', 'filetwo', '-b', 'filethree'])
# Namespace(a=True, b=True), ['fileone', 'filetwo', 'filethree']

ns = parser2.parse_args(rest, ns)
# Namespace(a=True, b=True, input=['fileone', 'filetwo', 'filethree'])

http://bugs.python.org/issue14191 は、次の1回の呼び出しでこれを実行する提案されたパッチです。

parser.parse_intermixed_args(['fileone', '-a', 'filetwo', '-b', 'filethree'])
3
hpaulj

Hpauljは正しい方向に進んでいるように見えますが、必要以上に複雑になっています。ブレアは古いoptparseモジュールの振る舞いに似たものを探していて、argsオブジェクトの入力フィールドに入力引数のリストを実際に必要としているのではないかと思います。彼はただ望んでいる

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-a', action='store_true')
parser.add_argument('-b', action='store_true')
opts, args = parser.parse_known_args(['fileone', '-a', 'filetwo', '-b', 'filethree'])
# Namespace(a=True, b=True), ['fileone', 'filetwo', 'filethree']

Optparseの方言では、「オプション」はoptsで利用可能であり、他の「引数」が散在している可能性のあるリストは引数で示されます。

2
Malcolm