web-dev-qa-db-fra.com

Python, argparse: comment avoir nargs = 2 avec type = str et type = int

J'ai passé quelques temps sur la documentation argparse, mais je continue de me battre avec ce module pour une option dans mon programme:

parser.add_argument("-r", "--rmsd", dest="rmsd", nargs=2,
    help="extract the poses that are close from a ref according RMSD",
    metavar=("ref","rmsd"))

Je voudrais que le premier argument soit une chaîne (type str) et obligatoire, tandis que le deuxième argument devrait avoir le type int, et si aucune valeur n'est donnée, en avoir un par défaut (disons dire default=50). Je sais comment faire cela quand il n'y a qu'un seul argument attendu, mais je ne sais pas comment procéder lorsque nargs = 2 ... Est-ce même possible?

18
Bux31

Vous pouvez effectuer les opérations suivantes. Le mot clé required définit le champ obligatoire et le default=50 définit la valeur par défaut de l'option à 50 si non spécifié:

import argparse

parser = argparse.ArgumentParser()

parser.add_argument("-s", "--string", type=str, required=True)
parser.add_argument("-i", "--integer", type=int, default=50)

args = parser.parse_args()    
print args.string
print args.integer

Production:

$ python arg_parser.py -s test_string
    test_string
    50
$ python arg_parser.py -s test_string -i 100
    test_string
    100
$ python arg_parser.py -i 100
    usage: arg_parser.py [-h] -s STRING [-i INTEGER]
    arg_parser.py: error: argument -s/--string is required
14
ashokadhikari

J'ai tendance à être d'accord avec la solution de Mike, mais voici une autre façon. Ce n'est pas idéal, car la chaîne utilisation/aide indique à l'utilisateur d'utiliser un ou plusieurs arguments.

import argparse

def string_integer(int_default):
    """Action for argparse that allows a mandatory and optional
    argument, a string and integer, with a default for the integer.

    This factory function returns an Action subclass that is
    configured with the integer default.
    """
    class StringInteger(argparse.Action):
        """Action to assign a string and optional integer"""
        def __call__(self, parser, namespace, values, option_string=None):
            message = ''
            if len(values) not in [1, 2]:
                message = 'argument "{}" requires 1 or 2 arguments'.format(
                    self.dest)
            if len(values) == 2:
                try:
                    values[1] = int(values[1])
                except ValueError:
                    message = ('second argument to "{}" requires '
                               'an integer'.format(self.dest))
            else:
                values.append(int_default)
            if message:
                raise argparse.ArgumentError(self, message)            
            setattr(namespace, self.dest, values)
    return StringInteger

Et avec ça, vous obtenez:

>>> import argparse
>>> parser = argparse.ArgumentParser(description="")
parser.add_argument('-r', '--rmsd', dest='rmsd', nargs='+',
...                         action=string_integer(50),
...                         help="extract the poses that are close from a ref "
...                         "according RMSD")
>>> parser.parse_args('-r reference'.split())
Namespace(rmsd=['reference', 50])
>>> parser.parse_args('-r reference 30'.split())
Namespace(rmsd=['reference', 30])
>>> parser.parse_args('-r reference 30 3'.split())
usage: [-h] [-r RMSD [RMSD ...]]
: error: argument -r/--rmsd: argument "rmsd" requires 1 or 2 arguments
>>> parser.parse_args('-r reference 30.3'.split())
usage: [-h] [-r RMSD [RMSD ...]]
: error: argument -r/--rmsd: second argument to "rmsd" requires an integer
9
user707650

Désolé d'avoir sauté trop tard. J'utiliserais une fonction de type à appeler.

def two_args_str_int(x):
    try:
        return int(x)
    except:
        return x

parser.add_argument("-r", "--rmsd", dest="rmsd", nargs=2, type=two_args_str_int
    help="extract the poses that are close from a ref according RMSD",
    metavar=("ref","rmsd"))
1
Grant Mills

Je recommanderais d'utiliser deux arguments:

import argparse

parser = argparse.ArgumentParser(description='Example with to arguments.')

parser.add_argument('-r', '--ref', dest='reference', required=True,
                    help='be helpful')
parser.add_argument('-m', '--rmsd', type=int, dest='reference_msd',
                    default=50, help='be helpful')

args = parser.parse_args()
print args.reference
print args.reference_msd
1
Mike Müller

J'ai eu un problème similaire, mais l'approche "utiliser deux arguments" n'a pas fonctionné pour moi car j'ai besoin d'une liste de paires: parser.add_argument('--replace', nargs=2, action='append') et si j'utilise des arguments séparés, je devrais valider la longueur des listes, etc. . Voici ce que j'ai fait:

  1. Utilisez Tuple pour metavar pour afficher correctement l'aide: Tuple=('OLD', 'NEW') entraîne l'affichage de la chaîne d'aide sous la forme --replace OLD NEW. Il est documenté mais je ne l'ai pas trouvé avant d'avoir essayé différentes options.
  2. Utilisez la validation personnalisée: après parse_args, Validez les éléments de la liste résultante et appelez parser.error() si quelque chose ne va pas. C'est parce qu'ils ont différents types de données.
0
MarSoft