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é
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])
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.
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.
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
Une autre solution consiste à passer l'argument en utilisant le symbole '=
' en plus de citer l'argument-c'est-à-dire --xlim="-2.3e14"
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.
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.