web-dev-qa-db-ja.com

Python Argparse:負の数であるオプションの引数の問題

argparseに小さな問題があります。プロットのxlimであるオプションxrangeがあります。 -2e-5のような番号を渡せるようにしたい。ただし、これは機能しません-argparseは、これが位置引数であると解釈します。 -0.00002を実行すると、機能します。argparseはそれを負の数として読み取ります。 -2e-3を読み取ることはできますか?

コードは以下のとおりです。実行方法の例は次のとおりです。

./blaa.py --xlim -2.e-3 1e4 

私が以下を行う場合、それは機能します:

./blaa.py --xlim -0.002 1e4 

コード:

parser.add_argument('--xlim', nargs = 2,
                  help = 'X axis limits',
                  action = 'store', type = float, 
                  default = [-1.e-3, 1.e-3])

このように機能させることはできますが、実際には科学的記数法を使用できるようにしたいと思います。誰かアイデアはありますか?

乾杯

29
Ger

コメントですでに指摘されているように、問題は、-プレフィックスが引数ではなくオプションとして解析されることです。これを回避する1つの方法は、オプションに使用されるプレフィックスを prefix_chars 引数で変更することです。

#!/usr/bin/python
import argparse

parser = argparse.ArgumentParser(prefix_chars='@')
parser.add_argument('@@xlim', nargs = 2,
                  help = 'X axis limits',
                  action = 'store', type = float,
                  default = [-1.e-3, 1.e-3])
print parser.parse_args()

出力例:

$ ./blaa.py @@xlim -2.e-3 1e4
Namespace(xlim=[-0.002, 10000.0])

編集:または、-を区切り文字として使用し続け、xlimを単一の値として渡し、typeの関数を使用して独自の解析を実装することもできます。

#!/usr/bin/python
import argparse

def two_floats(value):
    values = value.split()
    if len(values) != 2:
        raise argparse.ArgumentError
    values = map(float, values)
    return values

parser = argparse.ArgumentParser()
parser.add_argument('--xlim', 
                  help = 'X axis limits',
                  action = 'store', type=two_floats,
                  default = [-1.e-3, 1.e-3])
print parser.parse_args()

出力例:

$ ./blaa.py --xlim "-2e-3 1e4"
Namespace(xlim=[-0.002, 10000.0])
11
jcollado

私が見つけた回避策の1つは、値を引用することですが、スペースを追加します。あれは、

./blaa.py --xlim " -2.e-3" 1e4

このように、argparseは、最初の文字がハイフンダッシュではないため、-2.e-3がオプション名であるとは見なしませんが、float(string)は左側のスペースを無視するため、floatに適切に変換されます。

28
itub

別の回避策は、引数を引用することに加えて、「=」記号を使用して引数を渡すことです-つまり、--xlim="-2.3e14"

6
toes

これが私が使用するコードです。 (これはjeremiahbuddhaのものと似ていますが、負の数を扱うため、質問に直接答えます。)

argparse.ArgumentParser()を呼び出す前にこれを置いてください

for i, arg in enumerate(sys.argv):
  if (arg[0] == '-') and arg[1].isdigit(): sys.argv[i] = ' ' + arg
5
andrewfn

オプションの値を等号で指定すると、-で始まっていても、argparseはそれを個別のオプションとして扱いません。

./blaa.py --xlim='-0.002 1e4'
# As opposed to --xlim '-0.002 1e4'

また、値にスペースが含まれていない場合は、引用符を削除できます。

./blaa.py --xlim=-0.002

参照: https://www.gnu.org/software/guile/manual/html_node/Command-Line-Format.html

これにより、受け入れられた回答が示すように、独自のtype=パーサーを記述したり、プレフィックス文字を-から@に再定義したりする必要はありません。

4
mxxk

_argparse.py_自体を変更する場合は、負の数のマッチャーを変更して科学的記数法を処理できます。

class _ActionsContainer.__init__()

_self._negative_number_matcher = _re.compile(r'^-(\d+\.?|\d*\.\d+)([eE][+\-]?\d+)?$')
_

または、パーサーを作成した後、_parser._negative_number_matcher_をこの値に設定できます。このアプローチは、グループまたはサブパーサーを作成している場合に問題が発生する可能性がありますが、単純なパーサーで機能するはずです。

4
hpaulj

Andrewfnのアプローチに触発されて、私はsys.argvをいじる別のヘルパー関数を作成しました。

def _Tweak_neg_scinot():
    import re
    import sys
    p = re.compile('-\\d*\\.?\\d*e', re.I)
    sys.argv = [' ' + a if p.match(a) else a for a in sys.argv]

正規表現は以下を探します:

  • -:負の符号
  • \\d*:0桁以上(-.5e-2-4354.5e-6などの奇妙な形式の値の場合)
  • \\.?:オプションの期間(例:-2e-5は妥当です)
  • \\d*:0桁以上の別のセット(-2e-5-7.e-3などの場合)
  • e:指数マーカーと一致させる

re.Iは、-2e-5-2E-5の両方に一致します。 p.matchを使用すると、各文字列の先頭からのみ検索されます。

1
hBy2Py