11

Парсинг булевых значений с помощью argparse

17

Я хотел бы использовать библиотеку 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("") должно возвращать False.

Как мне заставить argparse правильно парсить значения "False", "F" и их аналоги в нижнем регистре как False?

5 ответ(ов)

3

Если вы хотите одновременно разрешить --feature и --no-feature (последний имеет приоритет)

Это позволит пользователям создать алиас командной строки с --feature, который можно будет переопределить с помощью --no-feature.

Python 3.9 и выше

parser.add_argument('--feature', default=True, action=argparse.BooleanOptionalAction)

Python 3.8 и ниже

Я рекомендую ответ mgilson:

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

Если вы НЕ хотите разрешать одновременно --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')
1

Вот еще одна вариация, которая не требует дополнительных строк для установки значений по умолчанию. Булевое значение всегда присваивается, чтобы его можно было использовать в логических операциях без предварительной проверки:

import argparse
parser = argparse.ArgumentParser(description="Разбор булевого значения")
parser.add_argument("--do-something", default=False, action="store_true",
                    help="Флаг для выполнения действий")
args = parser.parse_args()

if args.do_something:
     print("Выполняем действие")
else:
     print("Действие не выполняется")

print(f"Проверьте, что args.do_something={args.do_something} всегда является булевым значением.")

Таким образом, параметр --do-something будет равен True, если он указан в командной строке, и False в противном случае, обеспечивая корректное булевое значение.

0

Самый простой и правильный способ сделать это:

from distutils.util import strtobool

parser.add_argument('--feature', dest='feature',
                    type=lambda x: bool(strtobool(x)))

Обратите внимание, что истинные значения — это y, yes, t, true, on и 1; ложные значения — это n, no, f, false, off и 0. Если значение не соответствует ни одному из этих вариантов, будет вызвано исключение ValueError.

0

Вы можете использовать следующий однострочник для добавления аргумента --is_debug, который будет интерпретировать строку 'true' как True, а все остальные значения как False:

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

Существует некоторая путаница относительно того, что именно означает type=bool и type='bool'. Должен ли один (или оба) из этих параметров означать "выполнить функцию bool()" или "возвратить логическое значение"? На данный момент 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
  return v.lower() in ("yes", "true", "t", "1")
p = argparse.ArgumentParser()
p.register('type','bool',str2bool) # добавляем ключевое слово в реестр
p.add_argument('-b',type='bool')  # не используйте 'type=bool'
# p.add_argument('-b',type=str2bool) # работает так же хорошо
p.parse_args('-b false'.split())
Namespace(b=False)

Метод parser.register() не задокументирован, но и не скрыт. В большинстве случаев программисту не нужно о нем беспокоиться, поскольку параметры type и action могут принимать значения функций и классов. В интернете можно найти множество примеров на StackOverflow о том, как определить свои собственные значения для обоих параметров.


Если это не очевидно из предыдущего обсуждения, bool() не означает "разобрать строку". Согласно документации Python:

bool(x): Преобразует значение в логическое, используя стандартную процедуру проверки истинности.

Контрастируйте это с:

int(x): Преобразует число или строку x в целое число.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь