Saltar al contenido

Argparse: cómo manejar un número variable de argumentos (nargs = “*”)

Solución:

El error de Python relevante es el problema 15112.

argparse: nargs="*" El argumento posicional no acepta ningún elemento si está precedido por una opción y otro posicional

Cuando argparse analiza ['1', '2', '--spam', '8', '8', '9'] primero intenta coincidir ['1','2'] con tantos argumentos posicionales como sea posible. Con sus argumentos, la cadena de coincidencia de patrones es AAA*: 1 argumento cada uno para pos y fooy cero argumentos para vars (recordar * significa ZERO_OR_MORE).

['--spam','8'] son manejados por su --spam argumento. Ya que vars ya se ha configurado en [], no queda nada por manejar ['8','9'].

La programación cambia a argparse comprueba el caso donde 0 cadenas de argumentos satisfacen el patrón, pero todavía hay optionals para ser analizado. Luego pospone el manejo de ese * argumento.

Es posible que pueda solucionar esto analizando primero la entrada con parse_known_args, y luego manejando el remainder con otra llamada a parse_args.

Para tener total libertad en intercalar opcionales entre posicionales, en el número 14191, propongo usar parse_known_args con solo el optionals, seguido de un parse_args que solo sabe de los posicionales. los parse_intermixed_args función que publiqué allí podría implementarse en una ArgumentParser subclase, sin modificar el argparse.py código en sí.


Esta es una forma de manejar subanálisis. He tomado el parse_known_intermixed_args función, lo simplificó por motivos de presentación, y luego lo convirtió en el parse_known_args función de una subclase del analizador. Tuve que dar un paso más para evitar la recursividad.

Finalmente cambié el _parser_class de los sub analizadores Action, por lo que cada sub analizador utiliza esta alternativa parse_known_args. Una alternativa sería la subclase _SubParsersAction, posiblemente modificando su __call__.

from argparse import ArgumentParser

def parse_known_intermixed_args(self, args=None, namespace=None):
    # self - argparse parser
    # simplified from http://bugs.python.org/file30204/test_intermixed.py
    parsefn = super(SubParser, self).parse_known_args # avoid recursion

    positionals = self._get_positional_actions()
    for action in positionals:
        # deactivate positionals
        action.save_nargs = action.nargs
        action.nargs = 0

    namespace, remaining_args = parsefn(args, namespace)
    for action in positionals:
        # remove the empty positional values from namespace
        if hasattr(namespace, action.dest):
            delattr(namespace, action.dest)
    for action in positionals:
        action.nargs = action.save_nargs
    # parse positionals
    namespace, extras = parsefn(remaining_args, namespace)
    return namespace, extras

class SubParser(ArgumentParser):
    parse_known_args = parse_known_intermixed_args

parser = ArgumentParser()
parser.add_argument('foo')
sp = parser.add_subparsers(dest="cmd")
sp._parser_class = SubParser # use different parser class for subparsers
spp1 = sp.add_parser('cmd1')
spp1.add_argument('-x')
spp1.add_argument('bar')
spp1.add_argument('vars',nargs="*")

print parser.parse_args('foo cmd1 bar -x one 8 9'.split())
# Namespace(bar="bar", cmd='cmd1', foo='foo', vars=['8', '9'], x='one')

Para quien no sepa lo que es nargs:

nargs representa Number Of Arguments

  • 3: 3 valores, puede ser cualquier número que desee
  • ?: un valor único, que puede ser opcional
  • *: un número flexible de valores, que se recopilarán en una lista
  • +: como *, pero requiere al menos un valor
  • argparse.REMAINDER: todos los valores que quedan en la línea de comando

Ejemplo:

Pitón

import argparse

my_parser = argparse.ArgumentParser()
my_parser.add_argument('--input', action='store', type=int, nargs=3)

args = my_parser.parse_args()

print(args.input)

Consola

$ python nargs_example.py --input 42
usage: nargs_example.py [-h] [--input INPUT INPUT INPUT]
nargs_example.py: error: argument --input: expected 3 arguments

$ python nargs_example.py --input 42 42 42
[42, 42, 42]

Ver más

Solución simple: especifique el --spam bandera antes de especificar pos y foo:

p = argparse.ArgumentParser()
p.add_argument('pos')
p.add_argument('foo')
p.add_argument('--spam', default=24, type=int, dest="spam")
p.add_argument('vars', nargs="*")

p.parse_args('--spam 8 1 2 8 9'.split())

Lo mismo funciona si coloca el --spam bandera después de especificar sus argumentos variables.

p = argparse.ArgumentParser()
p.add_argument('pos')
p.add_argument('foo')
p.add_argument('--spam', default=24, type=int, dest="spam")
p.add_argument('vars', nargs="*")

p.parse_args('1 2 8 9 --spam 8'.split())

EDITAR: Por lo que vale, parece que cambiar el * a un + también solucionará el error.

p = argparse.ArgumentParser()
p.add_argument('pos')
p.add_argument('foo')
p.add_argument('--spam', default=24, type=int, dest="spam")
p.add_argument('vars', nargs="+")

p.parse_args('1 2 --spam 8 8 9'.split())
¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *