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])
Пока я могу заставить его работать таким образом, я бы скорее использовал научную нотацию. У кого-нибудь есть идеи?
Приветствия
Ответы
Ответ 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])
Ответ 2
Один способ обхода, который я нашел, - это указать значение, но добавив пробел. То есть
./blaa.py --xlim " -2.e-3" 1e4
Таким образом, argparse не будет думать, что -2.e-3 - это имя параметра, потому что первый символ не является дефис-тире, но он все равно будет правильно преобразован в float, потому что float (string) игнорирует пробелы на влево.
Ответ 3
Если вы хотите изменить argparse.py
самостоятельно, вы можете изменить совпадение отрицательных чисел для обработки научной нотации:
В class _ActionsContainer.__init__()
self._negative_number_matcher = _re.compile(r'^-(\d+\.?|\d*\.\d+)([eE][+\-]?\d+)?$')
Или после создания синтаксического анализатора вы можете установить для этого значения значение parser._negative_number_matcher
. Этот подход может иметь проблемы, если вы создаете группы или подпарамеры, но должны работать с простым парсером.
Ответ 4
Вот код, который я использую. (Он похож на jeremiahbuddha, но он отвечает на вопрос более непосредственно, поскольку он имеет дело с отрицательными числами.)
Поместите это перед вызовом argparse.ArgumentParser()
for i, arg in enumerate(sys.argv):
if (arg[0] == '-') and arg[1].isdigit(): sys.argv[i] = ' ' + arg
Ответ 5
Другим обходным путем является передача аргумента с использованием символа '=
' в дополнение к цитированию аргумента - т.е. --xlim="-2.3e14"
Ответ 6
Вдохновленный подходом andrewfn, я создал отдельную вспомогательную функцию, чтобы выполнить sys.argv
fiddling:
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*
: ноль или более цифр (для странно отформатированных значений, таких как -.5e-2
или -4354.5e-6
)
-
\\.?
: необязательный период (например, -2e-5
является разумным)
-
\\d*
: еще один набор из нуля или более цифр (для таких вещей, как -2e-5
и -7.e-3
)
-
e
: чтобы соответствовать маркеру экспоненты
re.I
делает его совпадающим как с -2e-5
, так и с -2e-5
. Использование p.match
означает, что он выполняет поиск только с начала каждой строки.