Разбор логических значений с помощью argparse
Я хотел бы использовать argparse для анализа логических аргументов командной строки, написанных как "--foo True" или "--foo False". Например:
my_program --my_boolean_flag False
однако следующий тестовый код не делает то, что я хотел бы:
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)
к сожалению, parsed_args.my_bool
значение True
. Это происходит даже тогда, когда я меняю cmd_line
на ["--my_bool", ""]
, что удивительно, поскольку bool("")
evalutates to False
.
как я могу получить argparse для разбора "False"
,"F"
, и их строчные варианты должны быть False
?
13 ответов:
еще одно решение с использованием предыдущих предложений, но с" правильной " ошибкой разбора от
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.')
Это очень полезно, чтобы сделать переключение со значениями по умолчанию; например
parser.add_argument("--nice", type=str2bool, nargs='?', const=True, default=NICE, help="Activate nice mode.")
позволяет мне использовать:
script --nice script --nice <bool>
и по-прежнему использовать значение по умолчанию (в зависимости от настроек пользователя). Один (косвенно связанный) недостаток этого подхода заключается в том, что "Нарги" могут поймать позиционный аргумент-см. это связано вопрос и это сообщение об ошибке argparse.
Я думаю, что более канонический способ сделать это через:
command --feature
и
command --no-feature
argparse
красиво поддерживает эту версию:parser.add_argument('--feature', dest='feature', action='store_true') parser.add_argument('--no-feature', dest='feature', action='store_false') parser.set_defaults(feature=True)
конечно, если вы действительно хотите
--arg <True|False>
версия, вы могли бы пройтиast.literal_eval
как "тип", или определяемая пользователем функция ...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?
Я рекомендую ответ mgilson, но с взаимоисключающей группой
так что вы не можете использовать--feature
и--no-feature
в то же время.command --feature
и
command --no-feature
а не
command --feature --no-feature
сценарий:
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)
там, кажется, некоторая путаница относительно того, что
type=bool
иtype='bool'
может означать. Если один (или оба) означает ' запустить функциюbool()
, или 'return a boolean'? Как он стоитtype='bool'
ничего не значит.add_argument
дает'bool' is not callable
ошибки, так же, как если бы вы использовалиtype='foobar'
илиtype='int'
.но
argparse
есть реестр, который позволяет определять ключевые слова, как это. Он в основном используется дляaction
, например 'action= 'store_true'. Вы можете увидеть зарегистрированные ключевые слова с:parser._registries
, который отображает словарь
{'action': {None: argparse._StoreAction, 'append': argparse._AppendAction, 'append_const': argparse._AppendConstAction, ... 'type': {None: <function argparse.identity>}}
есть много определенных действий, но только один тип, по умолчанию,
argparse.identity
.этот код определяет ключевое слово' 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()
не задокументирован,но и не скрыт. По большей части программисту не нужно об этом знать, потому чтоtype
иaction
принимать значения функций и классов. Есть много примеров stackoverflow определения пользовательских значения для обоих.
в случае, если это не очевидно из предыдущих рассуждений,
bool()
не означает "разбирать строку". Из документации Python:bool (x): преобразование значения в логическое значение с помощью стандартной процедуры проверки истинности.
сравните это с
int (x): преобразование числа или строки x в целое число.
Я искал ту же проблему, и ИМХО красивое решение:
def str2bool(v): return v.lower() in ("yes", "true", "t", "1")
и используя это, чтобы разобрать строку на boolean, как было предложено выше.
oneliner:
parser.add_argument('--is_debug', default=False, type=lambda x: (str(x).lower() == 'true'))
вот еще один вариант без дополнительной строки / s для установки значений по умолчанию. Bool всегда имеет назначенное значение, чтобы его можно было использовать в логических операторах без предварительных проверок.
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")
В дополнение к тому, что сказал @mgilson, следует отметить, что есть также
ArgumentParser.add_mutually_exclusive_group(required=False)
метод, который сделал бы его тривиальным для обеспечения этого--flag
и--no-flag
не используются одновременно.
это работает для всего, что я ожидаю:
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
код:
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')
проще было бы использовать, как показано ниже.
parser.add_argument('--feature', type=lambda s: s.lower() in ['true', 't', 'yes', '1'])
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)