Python Argparse: Problema con argumentos opcionales que son números negativos


Estoy teniendo un pequeño problema con argparse. Tengo una opción xlim que es el xrange de una parcela. Quiero poder pasar números como -2e-5. Sin embargo esto no funciona - argparse interpreta que este es un argumento posicional. Si lo hago -0.00002 funciona: argparse lo lee como un número negativo. ¿Es posible tener capaz de leer en -2e-3?

El código está a continuación, y un ejemplo de cómo lo ejecutaría es:

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

Si hago lo siguiente funciona: {[12]]}

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

El código:

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

Si bien puedo hacer que funcione de esta manera, realmente preferiría poder usar la notación científica. Alguien tiene alguna idea?

Salud

Author: Jens Höpken, 2012-01-27

7 answers

Como ya se señaló en los comentarios, el problema es que un prefijo - se analiza como una opción en lugar de como un argumento. Una forma de solucionar esto es cambiar el prefijo utilizado para las opciones con prefix_chars argumento:

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

Ejemplo de salida:

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

Editar: Alternativamente, puede seguir usando - como separador, pasar xlim como un solo valor y usar una función en type para implementar su propio análisis:

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

Ejemplo de salida:

$ ./blaa.py --xlim "-2e-3 1e4"
Namespace(xlim=[-0.002, 10000.0])
 11
Author: jcollado,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-27 12:07:32

Una solución que he encontrado es citar el valor, pero añadiendo un espacio. Es decir,

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

De esta manera argparse no pensará -2.e-3 es un nombre de opción porque el primer carácter no es un guion, pero aún así se convertirá correctamente a un flotador porque float(cadena) ignora los espacios de la izquierda.

 23
Author: itub,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-21 13:13:45

Si está dispuesto a modificar argparse.py en sí, podría cambiar el matcher de números negativos para manejar la notación científica:

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

O después de crear el analizador, podría establecer parser._negative_number_matcher a este valor. Este enfoque puede tener problemas si está creando grupos o subparsers, pero debería funcionar con un analizador simple.

 4
Author: hpaulj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-16 03:41:48

Aquí está el código que uso. (Es similar a jeremiahbuddha, pero responde a la pregunta más directamente, ya que se trata de números negativos.)

Pon esto antes de llamar argparse.ArgumentParser()

for i, arg in enumerate(sys.argv):
  if (arg[0] == '-') and arg[1].isdigit(): sys.argv[i] = ' ' + arg
 4
Author: andrewfn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-30 02:36:14

Otra solución es pasar el argumento usando '=' símbolo además de citar el argumento - es decir, --xlim="-2.3e14"

 4
Author: toes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-09 18:06:26

Inspirado por el enfoque de andrewfn, creé una función auxiliar separada para hacer el sys.argv jugueteo:

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 expresión regular busca:

  • - : un signo negativo
  • \\d*: cero o más dígitos (para valores con formato extraño como -.5e-2 o -4354.5e-6)
  • \\.?: un período opcional (por ejemplo, -2e-5 es razonable)
  • \\d*: otro conjunto de cero o más dígitos (para cosas como -2e-5 y -7.e-3)
  • e: para que coincida con el exponente marcador

re.I hace que coincida con -2e-5 y -2E-5. Usar p.match significa que solo busca desde el comienzo de cada cadena.

 1
Author: hBy2Py,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-24 04:45:48

Si especifica el valor de su opción con un signo igual, argparse no la tratará como una opción separada, incluso si comienza con -:

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

Y si el valor no tiene espacios en él, puede soltar las comillas:

./blaa.py --xlim=-0.002

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

Con esto, no hay necesidad de escribir su propio analizador type= o redefinir el carácter de prefijo de - a @ como la respuesta aceptada sugerir.

 1
Author: mxk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-02 00:19:39