Analyse des valeurs booléennes avec argparse

j'aimerais utiliser argparse pour analyser les arguments booléens en ligne de commande écrits comme" --foo True "ou"--foo False". Par exemple:

my_program --my_boolean_flag False

cependant, le code d'essai suivant 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)

tristement, parsed_args.my_bool évalue à True . C'est le cas même lorsque je change cmd_line pour ["--my_bool", ""] , ce qui est surprenant, puisque bool("") est évalué à False .

Comment obtenir argparse pour analyser "False" , "F" , et leurs variantes minuscules pour être False ?

365
demandé sur Peter Mortensen 2013-02-21 21:37:16

13 réponses

encore une autre solution en utilisant les suggestions précédentes, mais avec l'erreur" correcte " de argparse :

def str2bool(v):
    if v.lower() in ('yes', 'true', 't', 'y', '1'):
        return True
    elif v.lower() in ('no', 'false', 'f', 'n', '0'):
        return False
    else:
        raise argparse.ArgumentTypeError('Boolean value expected.')

c'est très utile pour faire des commutateurs avec des valeurs par défaut; par exemple

parser.add_argument("--nice", type=str2bool, nargs='?',
                        const=True, default=NICE,
                        help="Activate nice mode.")

me permet d'utiliser:

script --nice
script --nice <bool>

et toujours utiliser une valeur par défaut (paramètres de l'utilisateur). L'un des inconvénients (indirectement lié) de cette approche est que le "narguilé" pourrait prendre une position argument -- voir cette question connexe et ce rapport de bogue argparse .

108
répondu Maxim 2017-05-24 12:28:29

je pense que plus canonique façon de le faire est par le biais de:

command --feature

et

command --no-feature

argparse supporte cette version bien:

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 pouvez passer ast.literal_eval comme le" type", ou une fonction définie par l'utilisateur ...

def t_or_f(arg):
    ua = str(arg).upper()
    if 'TRUE'.startswith(ua):
       return True
    elif 'FALSE'.startswith(ua):
       return False
    else:
       pass  #error condition maybe?
577
répondu mgilson 2016-09-02 06:08:23

je recommande la réponse de mgilson mais avec un groupe mutuellement exclusif

de sorte que vous ne pouvez pas utiliser --feature et --no-feature en même temps.

command --feature

et

command --no-feature

mais pas

command --feature --no-feature

Script:

feature_parser = parser.add_mutually_exclusive_group(required=False)
feature_parser.add_argument('--feature', dest='feature', action='store_true')
feature_parser.add_argument('--no-feature', dest='feature', action='store_false')
parser.set_defaults(feature=True)
139
répondu fnkr 2017-06-01 12:04:02

il semble y avoir une certaine confusion quant à ce que type=bool et type='bool' pourrait signifier. Si l'un (ou les deux) signifie 'exécution de la fonction bool() , ou "retourner un booléen'? En l'état type='bool' ne signifie rien. add_argument donne une erreur 'bool' is not callable , comme si vous aviez utilisé type='foobar' , ou type='int' .

mais argparse a un registre qui vous permet de définir des mots clés comme ceci. Il est principalement utilisé pour 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 un seul type, celui par défaut, argparse.identity .

ce code définit un mot clé "bool":

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 parce que type et action prennent des valeurs de fonction et de classe. Il y a beaucoup d'exemples de stackoverflow pour définir des valeurs personnalisées pour les deux.


dans le cas où il n'est pas évident de la discussion précédente, bool() ne signifie pas 'parse une chaîne de caractères'. De la documentation Python:

bool (x): convertissez une valeur en booléen, en utilisant la procédure standard de test de vérité.

contraste avec

int (x): convertissez un nombre ou une chaîne x en un entier.

30
répondu hpaulj 2014-03-28 07:07:11

je cherchais la même question, et imho la jolie solution est:

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

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

18
répondu susundberg 2013-10-07 14:20:38

oneliner:

parser.add_argument('--is_debug', default=False, type=lambda x: (str(x).lower() == 'true'))
14
répondu Evalds Urtans 2017-10-26 09:44:45

Voici une autre variation sans ligne / S supplémentaire pour définir les valeurs par défaut. Le bool ont toujours une valeur assignée de sorte qu'il peut être utilisé dans des déclarations logiques sans pré-vérifications.

import argparse
parser = argparse.ArgumentParser(description="Parse bool")
parser.add_argument("--do-something", default=False, action="store_true" , help="Flag to do something")
args = parser.parse_args()

if args.do_something == True:
     print("Do something")
else:
     print("Don't do something")
print("Check that args.do_something=" + str(args.do_something) + " is always a bool")
14
répondu Schaki 2018-07-06 14:27:48

en plus de ce que @mgilson a dit, il faut noter qu'il y a aussi une méthode ArgumentParser.add_mutually_exclusive_group(required=False) qui rendrait futile de faire appliquer que --flag et --no-flag ne sont pas utilisés en même temps.

13
répondu foo 2014-03-28 04:20:17

cela fonctionne pour tout ce que j'attends de lui:

add_boolean_argument(parser, 'foo', default=True)
parser.parse_args([])                   # Whatever the default was
parser.parse_args(['--foo'])            # True
parser.parse_args(['--nofoo'])          # False
parser.parse_args(['--foo=true'])       # True
parser.parse_args(['--foo=false'])      # False
parser.parse_args(['--foo', '--nofoo']) # Error

le code:

def _str_to_bool(s):
    """Convert string to bool (in argparse context)."""
    if s.lower() not in ['true', 'false']:
        raise ValueError('Need bool; got %r' % s)
    return {'true': True, 'false': False}[s.lower()]

def add_boolean_argument(parser, name, default=False):                                                                                               
    """Add a boolean argument to an ArgumentParser instance."""
    group = parser.add_mutually_exclusive_group()
    group.add_argument(
        '--' + name, nargs='?', default=default, const=True, type=_str_to_bool)
    group.add_argument('--no' + name, dest=name, action='store_false')
8
répondu Stumpy Joe Pete 2016-03-24 06:09:20

Un moyen plus simple serait d'utiliser comme ci-dessous.

parser.add_argument('--feature', type=lambda s: s.lower() in ['true', 't', 'yes', '1'])
4
répondu arunkumarreddy 2017-04-30 12:08:28
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)
1
répondu Robert T. McGibbon 2014-10-28 21:00:19

une manière tout à fait similaire est d'utiliser:

feature.add_argument('--feature',action='store_true')

et si vous définissez l'argument -- fonctionnalité dans votre commande

 command --feature

l'argument sera True, si vous ne définissez pas type -- feature les arguments par défaut sont toujours False!

1
répondu dl.meteo 2016-03-16 09:42:46

je pense que la manière la plus canonique sera:

parser.add_argument('--ensure', nargs='*', default=None)

ENSURE = config.ensure is None
1
répondu Andreas Maertens 2017-04-30 12:09:15