#python #arguments #argparse #optional-arguments
Вопрос:
Я прочитал кучу вопросов, на которые уже даны ответы, но я не вижу, чтобы это было охвачено-по крайней мере, я не понял.
Я использую argparse, чтобы взять файл и преобразовать его в другой тип. Требуется ввести имя файла. Выходное имя файла НЕ требуется, так как необязательный аргумент должен обрабатывать это. Вот код до сих пор:
import sys
import argparse
parser = argparse.ArgumentParser(description='Convert file to new type')
parser.add_argument('--json', type=str, help='Converts to json format')
parser.add_argument('--bibtex', type=str, help='Converts to bibtex format')
parser.add_argument('--html', type=str, help='Converts to html format')
parser.add_argument('inputfilename', type=str, help='enter the original filename')
args = parser.parse_args()
filename=args.filename
if args.json:
print('Converting to json ...')
#conversion code here
elif args.bibtex:
print('Converting to bibtex ...')
#conversion code here
elif args.html:
print('Converting to html ...')
#conversion code here
else:
print('No conversion type indicated')
Проблема в том, что всякий раз, когда я использую один из этих флагов. Если я это сделаю
$ ./orsconvert.py --json inputfilename
Я получаю сообщение об ошибке, говорящее
orsconvert.py: error: the following arguments are required: inputfilename
Это происходит потому, что он интерпретирует предоставленный inputfilename
файл как имя выходного файла, подключенное к --json
. Он пытается заставить меня фактически указать выходное имя файла после необязательного аргумента и перед входным именем файла, аналогично этому:
$ ./orsconvert.py --json outputfilename inputfileneame
Однако я не хочу этого делать, если есть способ обойти это. Я хочу, чтобы он принимал --json
в качестве указания, что делать, а затем использовал inputfilename
в качестве входных данных и автоматически сохранял его в outputfilename
соответствии с тем, что будет указано в коде.
Да, я рассматриваю возможность объединения 3 необязательных аргументов в группу для упрощения кода … но это все еще не объясняет, как заставить его не требовать другого аргумента после необязательного и до окончательного inputfilename
требуемого аргумента.
Есть ли что-то еще, что я могу добавить в parser.add_argument('...')
поле, что перестанет требовать от меня указывать outputfilename
? Я хотел бы изменить код как можно меньше, потому что я уже раздвигаю границы своего понимания кода.
Комментарии:
1. Я не уверен, что эта настройка надежна. Мне кажется, что на самом деле вам нужны 1-2 имени файлов и «режим» вместо 1 имени файла, «режим» и необязательное «имя файла режима». Это должен быть выбор/подраздел для режима, позиционное имя файла и
nargs="?"
имя файла.2. Разве у вас не может быть необязательного имени файла вывода в качестве собственного аргумента и логических флагов для типа (игнорируя, что должно произойти, если указано более одного типа)?
Ответ №1:
Проблема в том, что вы определили --json
, что требуется аргумент, и в качестве этого аргумента используется имя входного файла. Ваши три конвертера должны использовать store_true
действие, а не store
действие по умолчанию.
parser = argparse.ArgumentParser(description='Convert file to new type')
parser.add_argument('--json', action='store_true', help='Converts to json format')
parser.add_argument('--bibtex', action='store_true', help='Converts to bibtex format')
parser.add_argument('--html', action='store_true', help='Converts to html format')
parser.add_argument('inputfilename', help='enter the original filename')
args = parser.parse_args()
С этим изменением args.json
et все являются логическими значениями, которые вы ожидаете от них. store_true
Действие заботится об определении типа bool
и значения по умолчанию false
.
Еще лучше, однако, был бы один обязательный позиционный аргумент, который должен принимать значение json
, bibtex
, или html
. Затем parse_args
он сам определит, был ли задан неправильный или отсутствующий тип преобразования.
parser = argparse.ArgumentParser(description='Convert file to new type')
parser.add_argument('inputfilename', help='enter the original filename')
parser.add_argument('conversion_type', choices=['json', 'bibtex', 'html'])
args = parser.parse_args()
filename = args.filename
conversion_type = args.conversion_type # Guaranteed to be json, html, or bibtex
if conversion_type == "json":
...
elif conversion_type == "bibtex":
...
elif conversion_type == "html":
Если вам нужна опция со значением по умолчанию, пристегнитесь 🙂 Мы собираемся добавить 4 опции: одну, которая позволяет явно указать тип вывода, и три ярлыка для его настройки.
p = argparse.ArgumentParser()
p.add_argument('inputfilename')
p.add_argument('--type', choices=['json', 'bibtex', 'html'], default='json')
p.add_argument('--json', action='store_const', const='json', dest='type')
p.add_argument('--html', action='store_const', const='html', dest='type')
p.add_argument('--bibtex', action='store_const', const='bibtex', dest='type')
--json
, --bibtex
, и --html
имеют тот же эффект, --type json
что и, --type bibtex
, и --type html
, соответственно. Как и прежде, --type
может принимать в качестве аргумента только эти три значения. Если их несколько, вступает в силу последний из них.
Комментарии:
1. Спасибо. Это помогает. Я не понимал, что, делая переменную необязательной с помощью — по умолчанию, она требует другого аргумента.
2. Только по умолчанию 🙂 Давайте сделаем шаг назад. Префикс имени аргумента с
--
делает его опцией, а не позиционным аргументом. Однако у каждого параметра есть соответствующее действие , которое'store'
по умолчанию. Это действие магазина, которое заставляет параметр требовать аргумент, если используется параметр.'store_const'
, с другой стороны, не требует аргумента, поскольку вы заранее указываете значение для хранения в определении аргумента.3. Итак, с практической точки зрения, почему я должен хотеть использовать «store_true», а не «store_const»? Мне нравится идея » выбор= […]», но мой руководитель хочет, чтобы я сохранил поля с дефисами «—json», «—bibtex», «—html», хотя требуется хотя бы одно.
4.
store_true
в основном это особый случайstore_const
.add_argument("...", action='store_true')
это сокращение отadd_argument("...", action='store_const', const=True, type=bool, default=False)
. (Существуетstore_false
также соответствующее действие.)5. Возможно, вы захотите взглянуть на docs.python.org/3/library/argparse.html#mutual-exclusion кроме того, это может гарантировать, что будет выбран именно один из трех вариантов, как
choices
и для одного варианта или аргумента.
Ответ №2:
argparse
уже поставляется со всем необходимым, чтобы на самом деле иметь нужный вам API, а не тот, который, по вашему мнению, вам нужен.
- Тип должен быть одним из нескольких
choices
, а не тремя отдельными флагами. - Это
inputfilename
должно быть обязательно. - Это
outputfilename
должно быть необязательно.
import argparse
parser = argparse.ArgumentParser(description='Convert file to new type')
parser.add_argument('format', choices=["json", "bibtex", "html"])
parser.add_argument('inputfilename', type=str)
parser.add_argument('outputfilename', type=str, nargs="?")
args = parser.parse_args()
print(args)
Это автоматически обеспечивает желаемое использование, не злоупотребляя опциями, но вместо этого получая обработку ошибок.
$ ./converter json in.txt
Namespace(format='json', inputfilename='in.txt', outputfilename=None)
$ ./converter html in.txt out.html
Namespace(format='html', inputfilename='in.txt', outputfilename='out.html')
$ ./converter yaml in.txt
usage: converter [-h] {json,bibtex,html} inputfilename [outputfilename]
converter: error: argument format: invalid choice: 'yaml' (choose from 'json', 'bibtex', 'html')
Комментарии:
1. Извините, я не хотел красть ваш ответ; Я думаю, он появился, когда я добавлял это же предложение.
2. @chepner, по крайней мере, мы не выбрали один и тот же порядок аргументов. 😉
3. Меня так и подмывает это украсть 🙂
Ответ №3:
Ваша проблема в том, что argparse ожидает ввода, тем более что у вас есть type=str
Попробуйте вместо этого заменить json arg на это
parser.add_argument('--json', action="store_true", help='Converts to json format')
Ответ №4:
Таким образом, проблема выглядит так, как будто каждый argparse.arg требует указания аргумента, будь то что угодно. Так что, возможно, вы могли бы установить его в логическое значение. Но я думаю, что, возможно, этот подход мог бы быть лучше:
import sys
import argparse
parser = argparse.ArgumentParser(description='Convert file to new type')
parser.add_argument('--type', type=str, help='Converts to any of the following formats: 1 for json, 2 for bibtex and 3 for html')
parser.add_argument('--inputfilename', type=str, help='enter the original filename')
args = parser.parse_args()
filename=args.inputfilename
if args.type:
conversion_type = int(args.type)
if conversion_type == int(1):
print('Converting to json ...')
#conversion code here
elif conversion_type == 2:
print('Converting to bibtex ...')
#conversion code here
elif conversion_type == 3:
print('Converting to html ...')
#conversion code here
else:
print('No conversion type indicated')
Просто добавьте аргумент для указания типа. Исходя из этого, конвертируйте или нет в соответствии с условиями.
Комментарии:
1. Я не думаю
type
иinputfilename
должен быть необязательным . Argparse уже поддерживает выбор, поэтому наличие какой-либо непрозрачной схемы нумерации (которая плохо сочетается с типами) также не совсем подходит.2. Наверное, я не знал об этом
action=store_true
варианте. Прошу прощения, если мой ответ ввел вас в заблуждение.3. @MisterMiyagi, я думаю, это правда