898 votes

analyser les valeurs booléennes avec argparse

Je voudrais utiliser argparse pour analyser boolean arguments de ligne de commande écrit "--foo Vrai" ou "--foo Faux". Par exemple:

my_program --my_boolean_flag False

Cependant, le test suivant code ne fait pas ce que je voudrais:

import argparse
parser = argparse.ArgumentParser(description="My parser")
parser.add_argument("--my_bool", type=bool)
cmd_line = ["--my_bool", "False"]
parsed_args = parser.parse(cmd_line)

Malheureusement, parsed_args.my_bool évalue True. C'est le cas même quand je change cmd_line être ["--my_bool", ""], ce qui est surprenant, car bool("") evalutates d' False.

Comment puis-je obtenir argparse pour analyser "False", "F", et de leurs bas-de-casse variantes à être False?

1211voto

mgilson Points 92954

Je pense qu'une manière plus canonique de le faire est via:

 command --feature
 

et

 command --no-feature
 

argparse supporte bien cette version:

 parser.add_argument('--feature', dest='feature', action='store_true')
parser.add_argument('--no-feature', dest='feature', action='store_false')
parser.set_defaults(feature=True)
 

Bien sûr, si vous voulez vraiment la version --arg <True|False> , vous pourriez passer ast.literal_eval comme "type", ou une fonction définie par l'utilisateur ...

 def t_or_f(arg):
    ua = str(arg).upper()
    if ua == 'TRUE'[:len(ua)]:
       return True
    elif ua == 'FALSE'[:len(ua)]:
       return False
    else:
       pass  #error condition maybe?
 

37voto

hpaulj Points 6132

Il semble y avoir une certaine confusion quant à ce qu' type=bool et type='bool' peut signifier. Si l'un (ou les deux) signifie 'exécution de la fonction bool(), ou de "retourner un booléen'? Comme il est type='bool' ne signifie rien. add_argument donne un 'bool' is not callable d'erreur, de même que si vous utilisiez type='foobar'ou type='int'.

Mais argparse n'ont de registre qui vous permet de définir des mots-clés comme ceci. Il est principalement utilisé pour l' action, par exemple `action='store_true'. Vous pouvez voir les mots clés:

parser._registries

qui affiche un dictionnaire

{'action': {None: argparse._StoreAction,
  'append': argparse._AppendAction,
  'append_const': argparse._AppendConstAction,
...
 'type': {None: <function argparse.identity>}}

Il y a beaucoup d'actions définies, mais d'un seul type, celui par défaut, argparse.identity.

Ce code définit une 'bool' mot-clé:

def str2bool(v):
  #susendberg's function
  return v.lower() in ("yes", "true", "t", "1")
p = argparse.ArgumentParser()
p.register('type','bool',str2bool) # add type keyword to registries
p.add_argument('-b',type='bool')  # do not use 'type=bool'
# p.add_argument('-b',type=str2bool) # works just as well
p.parse_args('-b false'.split())
Namespace(b=False)

parser.register() n'est pas documentée, mais aussi de ne pas cachés. Pour la plupart, le programmeur n'a pas besoin de le savoir, car type et action prendre la fonction et les valeurs de classe. Il y a beaucoup de stackoverflow exemples de définition des valeurs personnalisées pour les deux.


Dans le cas où il n'est pas évident à partir de la discussion précédente, en bool() ne signifie pas "analyser une chaîne de caractères". À partir de la documentation Python:

bool(x): permet de Convertir une valeur d'un Booléen, à l'aide de la norme de la vérité, la procédure d'essai.

Cela contraste avec la

int(x): convertit un nombre ou une chaîne de x à l'entier inférieur.

18voto

susundberg Points 185

Je cherchais le même problème, et à mon avis, la jolie solution est la suivante:

 def str2bool(v):
  return v.lower() in ("yes", "true", "t", "1")
 

et en utilisant cela pour analyser la chaîne à booléenne comme suggéré ci-dessus.

13voto

foo Points 120

En plus de ce que @mgilson a dit, il convient de noter qu’il existe également une méthode ArgumentParser.add_mutually_exclusive_group(required=False) qui rendrait inutile le fait que --flag et --no-flag ne soient pas utilisé en même temps.

0voto

Robert McGibbon Points 1453
class FlagAction(argparse.Action):
    # From http://bugs.python.org/issue8538

    def __init__(self, option_strings, dest, default=None,
                 required=False, help=None, metavar=None,
                 positive_prefixes=['--'], negative_prefixes=['--no-']):
        self.positive_strings = set()
        self.negative_strings = set()
        for string in option_strings:
            assert re.match(r'--[A-z]+', string)
            suffix = string[2:]
            for positive_prefix in positive_prefixes:
                self.positive_strings.add(positive_prefix + suffix)
            for negative_prefix in negative_prefixes:
                self.negative_strings.add(negative_prefix + suffix)
        strings = list(self.positive_strings | self.negative_strings)
        super(FlagAction, self).__init__(option_strings=strings, dest=dest,
                                         nargs=0, const=None, default=default, type=bool, choices=None,
                                         required=required, help=help, metavar=metavar)

    def __call__(self, parser, namespace, values, option_string=None):
        if option_string in self.positive_strings:
            setattr(namespace, self.dest, True)
        else:
            setattr(namespace, self.dest, False)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X