Подтвердить что ты не робот

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])

Пока я могу заставить его работать таким образом, я бы скорее использовал научную нотацию. У кого-нибудь есть идеи?

Приветствия

4b9b3361

Ответ 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 означает, что он выполняет поиск только с начала каждой строки.