11

私は argparse を使用しており、サブコマンドと位置引数を混在させようとしていますが、次の問題が発生しました。

このコードは正常に実行されます:

import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()

parser.add_argument('positional')
subparsers.add_parser('subpositional')

parser.parse_args('subpositional positional'.split())

上記のコードは引数を に解析しますNamespace(positional='positional')が、位置引数を nargs='?' に変更すると そのような:

import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()

parser.add_argument('positional', nargs='?')
subparsers.add_parser('subpositional')

parser.parse_args('subpositional positional'.split())

次のエラーが発生します。

usage: [-h] {subpositional} ... [positional]
: error: unrecognized arguments: positional

どうしてこれなの?

4

4 に答える 4

9

最初は jcollado と同じように考えていましたが、後続の (トップ レベルの) 位置引数に特定のnargs( nargs= None, nargs= integer) がある場合、期待どおりに動作するという事実があります。またはの場合nargsは失敗し、場合によっては の場合に失敗します。それで、何が起こっているのかを理解するためにコードに行きました。'?''*''+'

それは、引数が消費されるために分割される方法に要約されます。誰が何を取得するかを把握するために、 への呼び出しは、あなたの場合 (位置引数の場合、オプションの場合)parse_argsのような文字列で引数を要約し、アクションに応じて、その要約文字列と一致する正規表現パターンを生成します。およびメソッドを使用してパーサーに追加しました。'AA''A''O'.add_argument.add_subparsers

いずれの場合も、たとえば、引数文字列は最終的に'AA'. どのような変更が一致するパターンになります (可能性のあるパターンは の下_get_nargs_patternで確認できますargparse.pysubpositionalこれは最終的に になるため、1 つの引数の後に任意の数のオプションまたは引数が続くこと'(-*A[-AO]*)'を意味します。positionalnargs

  • None=>'(-*A-*)'
  • 3 => '(-*A-*A-*A-*)'('-*A'予想される引数ごとに 1 つ)
  • '?'=>'(-*A?-*)'
  • '*'=>'(-*[A-]*)'
  • '+'=>'(-*A[A-]*)'

これらのパターンが追加され、 for nargs=None(あなたの作業例) では、'(-*A[-AO]*)(-*A-*)'2 つの groups に一致する になります['A', 'A']。このようにして、(必要なもの)subpositionalのみを解析し、そのアクションに一致します。subpositionalpositional

ただしnargs='?'、の場合、最終的には になります'(-*A[-AO]*)(-*A?-*)'。2 番目のグループは完全にオプションのパターンで構成されており*、貪欲であるため、最初のグループは文字列内のすべてをグロブし、最終的に 2 つのグループを認識します['AA', '']。これはsubpositional2 つの引数を取得することを意味し、もちろん窒息してしまいます。

おかしなことに、 のパターンnargs='+''(-*A[-AO]*)(-*A[A-]*)'であり、引数を 1 つだけ渡す限り機能します。2番目subpositional aのグループで少なくとも 1 つの位置引数が必要なため、 とします。繰り返しますが、最初のグループは貪欲であるため、合格するsubpositional a b c dと が得られますが['AAAA', 'A']、これはあなたが望んでいたことではありません。

要するに:混乱。これはバグと考えるべきだと思いますが、パターンが非貪欲なものに変わった場合にどのような影響があるかはわかりません...

于 2011-12-29T18:41:54.003 に答える
6
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('positional', nargs='?')

subparsers = parser.add_subparsers()
subparsers.add_parser('subpositional')

print(parser.parse_args(['positional', 'subpositional']))
# -> Namespace(positional='positional')
print(parser.parse_args(['subpositional']))
# -> Namespace(positional=None)
parser.print_usage()
# -> usage: bpython [-h] [positional] {subpositional} ...

通常、コマンドの前 (左側) の引数はメイン プログラムに属し、後 (右側) の引数はコマンドに属します。したがってpositional、コマンドの前に置く必要がありsubpositionalます。プログラム例: git, twistd.

さらに、引数 withnarg=?はおそらくオプション ( --opt=value) であり、位置引数ではありません。

于 2011-12-29T18:59:08.417 に答える
5

問題は、add_subparsersが呼び出されたときに、サブパーサーの名前を渡すために元のパーサーに新しいパラメーターが追加されることだと思います。

たとえば、次のコードを使用します。

import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()

parser.add_argument('positional')                                             
subparsers.add_parser('subpositional')                                             

parser.parse_args()

次のヘルプ文字列が表示されます。

usage: test.py [-h] {subpositional} ... positional

positional arguments:
  {subpositional}
  positional

optional arguments:
  -h, --help       show this help message and exit

subpositionalの前に が表示されていることに注意してくださいpositional。あなたが探しているのは、サブパーサー名の前に位置引数があることだと思います。したがって、おそらく探しているのは、サブパーサーの前に引数を追加することです。

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('positional')

subparsers = parser.add_subparsers()
subparsers.add_parser('subpositional')

parser.parse_args()

このコードで得られるヘルプ文字列は次のとおりです。

usage: test.py [-h] positional {subpositional} ...

positional arguments:
  positional
  {subpositional}

optional arguments:
  -h, --help       show this help message and exit

このようにして、最初にメイン パーサーに引数を渡し、次にサブパーサーの名前を渡し、最後にサブパーサー (存在する場合) に引数を渡します。

于 2011-12-29T17:23:13.777 に答える
0

Python 3.5ではまだ混乱しています。

ArgumentParser をサブクラス化して、残りのすべての位置引数を保持し、後で処理することをお勧めします。

import argparse

class myArgumentParser(argparse.ArgumentParser):
    def parse_args(self, args=None, namespace=None):
       args, argv = self.parse_known_args(args, namespace)
       args.remaining_positionnals = argv
       return args

parser = myArgumentParser()

options = parser.parse_args()

残りの位置引数はリストにありますoptions.remaining_positionals

于 2016-01-08T23:05:43.940 に答える