Разбор логических значений с помощью 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
?
Ответы
Ответ 1
Еще одно решение с использованием предыдущих предложений, но с "правильной" ошибкой разбора от argparse
:
def str2bool(v):
if isinstance(v, bool):
return 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=False,
help="Activate nice mode.")
позволяет мне использовать:
script --nice
script --nice <bool>
и по-прежнему использовать значение по умолчанию (специфично для пользовательских настроек). Один (косвенно связанный) недостаток этого подхода заключается в том, что "nargs" может поймать позиционный аргумент - см. Этот связанный вопрос и этот отчет об ошибке argparse.
Ответ 2
Я думаю, что более канонический способ сделать это через:
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?
Ответ 3
Я рекомендую ответ 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)
Затем вы можете использовать этот помощник, если вы собираетесь установить многие из них:
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')
Ответ 4
Кажется, есть некоторая путаница в отношении того, что может означать type=bool
и type='bool'
. Если один (или оба) означает "запустить функцию bool()
или" вернуть логическое значение "? Поскольку он type='bool'
ничего не значит. add_argument
дает ошибку 'bool' is not callable
, как если бы вы использовали type='foobar'
или type='int'
.
Но argparse
имеет реестр, который позволяет вам определять ключевые слова, подобные этому. Он в основном используется для 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 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 в целое число.
Ответ 5
Вот еще один вариант без дополнительных строк/с для установки значений по умолчанию. Для 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:
print("Do something")
else:
print("Don't do something")
print("Check that args.do_something=" + str(args.do_something) + " is always a bool")
Ответ 6
Oneliner:
parser.add_argument('--is_debug', default=False, type=lambda x: (str(x).lower() == 'true'))
Ответ 7
Я искал одну и ту же проблему, а imho - это решение:
def str2bool(v):
return v.lower() in ("yes", "true", "t", "1")
и используя это для синтаксического анализа строки, как было предложено выше.
Ответ 8
В дополнение к тому, что сказал @mgilson, следует отметить, что существует также метод ArgumentParser.add_mutually_exclusive_group(required=False)
, который сделал бы его тривиальным для обеспечения того, чтобы --flag
и --no-flag
не используются одновременно.
Ответ 9
Аналогичным образом можно использовать:
feature.add_argument('--feature',action='store_true')
и если вы установите аргумент --feature в своей команде
command --feature
аргумент будет True, если вы не задаете тип --feature, аргументы default всегда False!
Ответ 10
Это работает для всего, что я ожидаю от него:
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')
Ответ 11
Более простой способ - использовать ниже.
parser.add_argument('--feature', type=lambda s: s.lower() in ['true', 't', 'yes', '1'])
Ответ 12
Я думаю, что самый канонический способ будет:
parser.add_argument('--ensure', nargs='*', default=None)
ENSURE = config.ensure is None
Ответ 13
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)
Ответ 14
Простейшим способом было бы использовать выбор:
parser = argparse.ArgumentParser()
parser.add_argument('--my-flag',choices=('True','False'))
args = parser.parse_args()
flag = args.my_flag == 'True'
print(flag)
Не передавая --my-flag, оценивается как False. Параметр required = True может быть добавлен, если вы всегда хотите, чтобы пользователь явно указал выбор.
Ответ 15
Быстро и просто, но только для аргументов 0 или 1:
parser.add_argument("mybool", default=True,type=lambda x: bool(int(x)))
myargs=parser.parse_args()
print(myargs.mybool)
После вызова с терминала вывод будет "False":
python myscript.py 0