usage mutually metavar float exclusive argumentparser argument add_argument python boolean argparse command-line-parsing

mutually - metavar python



Analizando valores booleanos con argparse (13)

Me gustaría usar argparse para analizar argumentos de línea de comandos booleanos escritos como "--foo True" o "--foo False". Por ejemplo:

my_program --my_boolean_flag False

Sin embargo, el siguiente código de prueba no hace lo que me gustaría:

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)

Lamentablemente, parsed_args.my_bool evalúa como True . Este es el caso incluso cuando cambio cmd_line para que sea ["--my_bool", ""] , lo cual es sorprendente, ya que bool("") evalúa como False .

¿Cómo puedo hacer que argparse para analizar "False" , "F" , y que sus variantes en minúsculas sean False ?



Aquí hay otra variación sin filas / s adicionales para establecer los valores predeterminados. El bool siempre tiene un valor asignado para que pueda usarse en declaraciones lógicas sin comprobaciones previas.

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")


Creo que la forma más canónica será:

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


Creo que una forma más canónica de hacerlo es a través de:

command --feature

y

command --no-feature

argparse soporta esta versión muy 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)

Por supuesto, si realmente desea la --arg <True|False> , podría pasar ast.literal_eval como el "tipo", o una función definida por el usuario ...

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?


Estaba buscando el mismo problema, y ​​ahora la solución bonita es:

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

y usar eso para analizar la cadena a booleano como se sugirió anteriormente.


Esto funciona para todo lo que espero:

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

El código:

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'')


Otra solución más que utiliza las sugerencias anteriores, pero con el error de análisis "correcto" 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.'')

Esto es muy útil para hacer interruptores con valores predeterminados; por ejemplo

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

me permite usar:

script --nice script --nice <bool>

y seguir utilizando un valor predeterminado (específico para la configuración del usuario). Un inconveniente (relacionado indirectamente) con ese enfoque es que los ''nargs'' pueden captar un argumento posicional: vea esta pregunta relacionada y este informe de error argparse .


Parece haber cierta confusión en cuanto a lo que podrían significar type=bool y type=''bool'' . ¿Debe uno (o ambos) significar ''ejecutar la función bool() , o'' devolver un booleano ''? En su forma actual, type=''bool'' no significa nada. add_argument da un error ''bool'' is not callable , igual que si add_argument type=''foobar'' , o type=''int'' .

Pero argparse tiene un registro que le permite definir palabras clave como esta. Se utiliza principalmente para la action , por ejemplo, `action = ''store_true''. Puedes ver las palabras clave registradas con:

parser._registries

que muestra un diccionario

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

Hay muchas acciones definidas, pero solo un tipo, el predeterminado, argparse.identity .

Este código define una palabra clave ''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() no está documentado, pero tampoco está oculto. En su mayor parte, el programador no necesita saberlo porque el type y la action toman valores de función y clase. Hay muchos ejemplos de para definir valores personalizados para ambos.

En caso de que no sea obvio en la discusión anterior, bool() no significa "analizar una cadena". De la documentación de Python:

bool (x): Convierte un valor a Booleano, usando el procedimiento de prueba de verdad estándar.

Contraste esto con

int (x): convierte un número o una cadena x en un entero.


Recomiendo la respuesta de mgilson pero con un grupo mutuamente exclusivo.
de modo que no puede usar --feature y --no-feature al mismo tiempo.

command --feature

y

command --no-feature

pero no

command --feature --no-feature

Guión:

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)

Luego puede usar este asistente si va a configurar muchos de ellos:

def add_bool_arg(parser, name, default=False): group = parser.add_mutually_exclusive_group(required=False) group.add_argument(''--'' + name, dest=name, action=''store_true'') group.add_argument(''--no-'' + name, dest=name, action=''store_false'') parser.set_defaults(**{name:default}) add_bool_arg(parser, ''useful-feature'') add_bool_arg(parser, ''even-more-useful-feature'')


Una forma bastante similar es usar:

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

y si establece el argumento --feature en su comando

command --feature

el argumento será Verdadero, si no establece el tipo --featura, los argumentos predeterminados siempre son Falso!


Una forma más sencilla sería utilizar como se muestra a continuación.

parser.add_argument(''--feature'', type=lambda s: s.lower() in [''true'', ''t'', ''yes'', ''1''])


un trazador de líneas:

parser.add_argument(''--is_debug'', default=False, type=lambda x: (str(x).lower() == ''true''))


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)