Python Argparse: problème avec des arguments optionnels qui sont des nombres négatifs

J'ai un petit problème avec argparse. J'ai une option {[4] } qui est la xrange d'un tracé. Je veux être capable de passer des nombres comme -2e-5. Cependant, cela ne fonctionne pas - argparse interprète ceci est un argument positionnel. Si je fais -0.00002 cela fonctionne: argparse le lit comme un nombre négatif. Est-il possible d'avoir capable de lire dans -2e-3?

Le code est ci-dessous, et un exemple de la façon dont je l'exécuterais est:

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

Si je fais ce qui suit, cela fonctionne:

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

Le code:

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

Bien que je puisse le faire fonctionner de cette façon, je préférerais vraiment pouvoir utiliser la notation scientifique. N'importe qui ont des idées?

Santé

25
demandé sur Jens Höpken 2012-01-27 00:55:56

7 réponses

Comme déjà souligné par les commentaires, le problème est qu'un préfixe - est analysé comme une option au lieu d'un argument. Une façon de contourner ce problème est de changer le préfixe utilisé pour les options avec prefix_chars argument:

#!/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()

Exemple de sortie:

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

Edit: alternativement, vous pouvez continuer à utiliser - comme séparateur, passer {[7] } comme une seule valeur et utiliser une fonction dans type pour implémenter votre propre analyse:

#!/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()

Exemple de sortie:

$ ./blaa.py --xlim "-2e-3 1e4"
Namespace(xlim=[-0.002, 10000.0])
11
répondu jcollado 2012-01-27 12:07:32

Une solution de contournement que j'ai trouvée est de citer la valeur, mais en ajoutant un espace. C'est-à-dire

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

De cette façon argparse ne pensera pas -2.e-3 est un nom d'option car le premier caractère n'est pas un tiret, mais il sera toujours converti correctement en flottant car float (string) ignore les espaces à gauche.

23
répondu itub 2013-06-21 13:13:45

Si vous êtes à la modification de argparse.py lui-même, vous pouvez changer le nombre négatif matcher pour gérer la notation scientifique:

Dans class _ActionsContainer.__init__()

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

Ou après avoir créé l'analyseur, vous pouvez définir parser._negative_number_matcher à cette valeur. Cette approche peut avoir des problèmes si vous créez des groupes ou des sous-parties, mais devrait fonctionner avec un analyseur simple.

4
répondu hpaulj 2013-03-16 03:41:48

Voici le code que j'utilise. (Il est similaire à jeremiahbuddha mais il répond à la question plus directement car il traite des nombres négatifs.)

Mettez ceci avant d'appeler argparse.ArgumentParser()

for i, arg in enumerate(sys.argv):
  if (arg[0] == '-') and arg[1].isdigit(): sys.argv[i] = ' ' + arg
4
répondu andrewfn 2014-01-30 02:36:14

Une autre solution consiste à passer l'argument en utilisant le symbole '= ' en plus de citer l'argument-c'est-à-dire --xlim="-2.3e14"

4
répondu toes 2014-12-09 18:06:26

Inspiré par l'approche d'andrewfn, j'ai créé une fonction d'aide distincte pour faire le 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]

La regex recherche:

  • - : un signe négatif
  • \\d*: zéro ou plus de chiffres (pour des valeurs au format étrange comme -.5e-2 ou -4354.5e-6)
  • \\.?: une période facultative (par exemple, -2e-5 est raisonnable)
  • \\d*: un autre ensemble de zéro ou plusieurs chiffres (pour des choses comme -2e-5 et -7.e-3)
  • e: pour faire correspondre l'exposant marqueur

re.I en fait correspondre à la fois à -2e-5 et -2E-5. L'utilisation de p.match signifie qu'elle ne recherche qu'à partir du début de chaque chaîne.

1
répondu hBy2Py 2016-10-24 04:45:48

Si vous spécifiez la valeur de votre option avec un signe égal, argparse Ne la traitera pas comme une option séparée, même si elle commence par -:

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

Et si la valeur n'a pas d'espaces, vous pouvez supprimer les guillemets:

./blaa.py --xlim=-0.002

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

Avec cela, il n'est pas nécessaire d'écrire votre propre analyseur type= ou de redéfinir le caractère de préfixe de - à @ comme réponse acceptée suggérer.

1
répondu mxk 2018-03-02 00:19:39