Учебник по Argparse¶
Автор: | Tshepang Lekhonkhobe |
---|
Это учебное пособие предназначено для того, чтобы быть мягким введением в
argparse
, рекомендуемый модуль командной строки парсинг в стандартной
библиотеке Python.
Примечание
Есть два других модуля, которые выполняют одну и ту же задачу, а именно:
getopt
(эквивалентный getopt()
из языка C) и устаревший
optparse
. Отметим также, что argparse
основан на optparse
и,
следовательно, очень похож с точки зрения использования.
Понятия¶
Давайте покажем, какую функциональность мы собираемся изучить в этом вводном руководстве, используя команду ls:
$ ls
cpython devguide prog.py pypy rm-unused-function.patch
$ ls pypy
ctypes_configure demo dotviewer include lib_pypy lib-python ...
$ ls -l
total 20
drwxr-xr-x 19 wena wena 4096 Feb 18 18:51 cpython
drwxr-xr-x 4 wena wena 4096 Feb 8 12:04 devguide
-rwxr-xr-x 1 wena wena 535 Feb 19 00:05 prog.py
drwxr-xr-x 14 wena wena 4096 Feb 7 00:59 pypy
-rw-r--r-- 1 wena wena 741 Feb 18 01:01 rm-unused-function.patch
$ ls --help
Usage: ls [OPTION]... [FILE]...
List information about the FILEs (the current directory by default).
Sort entries alphabetically if none of -cftuvSUX nor --sort is specified.
...
Несколько концепций, которые мы можем извлечь из четырех команд:
- Команда ls полезна при запуске без каких-либо параметров. По умолчанию отображается содержимое текущего каталога.
- Если мы хотим больше того, что он предоставляет по умолчанию, мы говорим это
немного больше. В этом случае мы хотим, чтобы он отображал другой каталог,
pypy
. Мы указали, что называется позиционным аргументом. Он назван так, потому что программа должна знать, что делать со значением, исключительно на основе того, где оно появляется в командной строке. Эта концепция более актуальна для такой команды, как cp, наиболее основное использование которой являетсяcp SRC DEST
. Первая позиция то что вы хотите скопированный и вторая позиция куда, вы хотите это, скопировать. - Скажем, мы хотим изменить поведение программы. В нашем примере мы показываем
больше информации для каждого файла вместо того, чтобы просто показывать имена
файлов. В этом случае
-l
называется необязательным аргументом. - Это фрагмент текста справки. Это очень полезно в том, что вы можете наткнуться на программу, которую вы никогда раньше не используемый, и можете выяснить, как она работает, просто прочитав ее справочный текст.
Основы¶
Начнем с очень простого примера, который (почти) ничего не делает:
import argparse
parser = argparse.ArgumentParser()
parser.parse_args()
Ниже приведен результат выполнения кода:
$ python3 prog.py
$ python3 prog.py --help
usage: prog.py [-h]
optional arguments:
-h, --help show this help message and exit
$ python3 prog.py --verbose
usage: prog.py [-h]
prog.py: error: unrecognized arguments: --verbose
$ python3 prog.py foo
usage: prog.py [-h]
prog.py: error: unrecognized arguments: foo
Вот что происходит:
- Выполнение сценария без каких-либо параметров не приводит к отображению stdout. Не так полезно.
- Второй начинает отображать полезность модуля
argparse
. Мы почти ничего не сделали, но уже получили хорошее сообщение о помощи. - Опция
--help
, которая также может быть сокращена до-h
, является единственной опцией, которую мы получаем бесплатно (т.е. нет необходимости указывать ее). Указание чего-либо другого приводит к ошибке. Но даже в этом случае мы получаем полезное сообщение об использовании, также бесплатно.
Представление позиционных аргументов¶
Пример:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("echo")
args = parser.parse_args()
print(args.echo)
И запуск кода:
$ python3 prog.py
usage: prog.py [-h] echo
prog.py: error: the following arguments are required: echo
$ python3 prog.py --help
usage: prog.py [-h] echo
positional arguments:
echo
optional arguments:
-h, --help show this help message and exit
$ python3 prog.py foo
foo
Вот что происходит:
- Мы добавили метод
add_argument()
, который мы используем для указания параметров командной строки, которые программа готова принять. В этом случае я назвал этоecho
так, чтобы это соответствовало своей функции. - Вызов программы требует указания опции.
- На самом деле
parse_args()
метод возвращает некоторые данные из указанных опций, в данном случаеecho
. - Переменная является некоторой формой «магии», которую
argparse
выполняет бесплатно (т.е. нет необходимости указывать, в какой переменной хранится это значение). Вы также заметите, что его имя совпадает с аргументом строка, предоставленным методуecho
.
Обратите внимание, что, хотя справочный дисплей выглядит хорошо и все, в
настоящее время он не так полезен, как может быть. Например, мы видим, что мы
получили echo
как позиционный аргумент, но мы не знаем, что он делает,
кроме как угадывая или читая источник код. Итак, давайте сделаем это
более полезным:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("echo", help="echo the string you use here")
args = parser.parse_args()
print(args.echo)
И получаем:
$ python3 prog.py -h
usage: prog.py [-h] echo
positional arguments:
echo echo the string you use here
optional arguments:
-h, --help show this help message and exit
Теперь, как насчет сделать что-то еще более полезным:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", help="display a square of a given number")
args = parser.parse_args()
print(args.square**2)
Ниже приведен результат выполнения кода:
$ python3 prog.py 4
Traceback (most recent call last):
File "prog.py", line 5, in <module>
print(args.square**2)
TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'
Это прошло не так хорошо. Это потому, что argparse
рассматривает варианты,
которые мы даем, как струны, если мы не скажем это иначе. Итак, давайте скажем
argparse
, чтобы рассматривать эти входные данные как целое число:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", help="display a square of a given number",
type=int)
args = parser.parse_args()
print(args.square**2)
Ниже приведен результат выполнения кода:
$ python3 prog.py 4
16
$ python3 prog.py four
usage: prog.py [-h] square
prog.py: error: argument square: invalid int value: 'four'
Все прошло хорошо. Теперь программа даже помогает выйти из-за неправильного нелегального ввода перед продолжением.
Представление дополнительных аргументов¶
Пока мы играем с позиционными аргументами. Давайте посмотрим, как добавить необязательные:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--verbosity", help="increase output verbosity")
args = parser.parse_args()
if args.verbosity:
print("verbosity turned on")
И вывод:
$ python3 prog.py --verbosity 1
verbosity turned on
$ python3 prog.py
$ python3 prog.py --help
usage: prog.py [-h] [--verbosity VERBOSITY]
optional arguments:
-h, --help show this help message and exit
--verbosity VERBOSITY
increase output verbosity
$ python3 prog.py --verbosity
usage: prog.py [-h] [--verbosity VERBOSITY]
prog.py: error: argument --verbosity: expected one argument
Вот что происходит:
- Программа записывается так, чтобы отображать что-то, когда
--verbosity
указан и ничего не отображать, когда нет. - Чтобы показать, что этот параметр является на самом деле необязательным, нет
ошибки при запуске программы без него. Обратите внимание, что по умолчанию, если
дополнительный аргумент не используется, соответствующей переменной, в этом
случае
args.verbosity
, даютNone
как стоимость, которая является причиной, это проваливает тест правдыif
инструкция. - Справочное сообщение немного отличается.
- При использовании опции
--verbosity
необходимо также указать некоторое значение, любое значение.
Вышеприведённый пример принимает произвольные целочисленные значения для
--verbosity
, но для нашей простой программы на самом деле полезны только два
значения, True
или False
. Давайте изменим код соответствующим
образом:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--verbose", help="increase output verbosity",
action="store_true")
args = parser.parse_args()
if args.verbose:
print("verbosity turned on")
И вывод:
$ python3 prog.py --verbose
verbosity turned on
$ python3 prog.py --verbose 1
usage: prog.py [-h] [--verbose]
prog.py: error: unrecognized arguments: 1
$ python3 prog.py --help
usage: prog.py [-h] [--verbose]
optional arguments:
-h, --help show this help message and exit
--verbose increase output verbosity
Вот что происходит:
- Этот параметр теперь больше флага, чем того, для чего требуется значение. Мы
даже изменили название варианта, чтобы соответствовать этой идее. Обратите
внимание, что теперь мы задаем новое ключевое слово
action
и придадим ему значение"store_true"
. Это означает, что, если опция указана, присвойте значениеTrue
параметруargs.verbose
. Не указание этого подразумеваетFalse
. - Он жалуется, когда вы указываете значение, в истинном духе того, что на самом деле флаги.
- Обратите внимание на другой текст справки.
Короткие опции¶
Если вы знакомы с использованием командной строки, вы заметите, что я еще не коснулся темы коротких версий параметров. Это довольно просто:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-v", "--verbose", help="increase output verbosity",
action="store_true")
args = parser.parse_args()
if args.verbose:
print("verbosity turned on")
И здесь goes:
$ python3 prog.py -v
verbosity turned on
$ python3 prog.py --help
usage: prog.py [-h] [-v]
optional arguments:
-h, --help show this help message and exit
-v, --verbose increase output verbosity
Обратите внимание, что новая возможность также отражена в тексте справки.
Объединение позиционных и необязательных аргументов¶
Наша программа постоянно усложняется:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
parser.add_argument("-v", "--verbose", action="store_true",
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbose:
print("the square of {} equals {}".format(args.square, answer))
else:
print(answer)
А теперь выход:
$ python3 prog.py
usage: prog.py [-h] [-v] square
prog.py: error: the following arguments are required: square
$ python3 prog.py 4
16
$ python3 prog.py 4 --verbose
the square of 4 equals 16
$ python3 prog.py --verbose 4
the square of 4 equals 16
- Мы вернули позиционный спор, отсюда и жалоба.
- Обратите внимание, что заказ не имеет значения.
Как насчет того, чтобы дать нашей программе возможность иметь несколько значений детальности, и на самом деле получить их использовать:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
parser.add_argument("-v", "--verbosity", type=int,
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
print("the square of {} equals {}".format(args.square, answer))
elif args.verbosity == 1:
print("{}^2 == {}".format(args.square, answer))
else:
print(answer)
И вывод:
$ python3 prog.py 4
16
$ python3 prog.py 4 -v
usage: prog.py [-h] [-v VERBOSITY] square
prog.py: error: argument -v/--verbosity: expected one argument
$ python3 prog.py 4 -v 1
4^2 == 16
$ python3 prog.py 4 -v 2
the square of 4 equals 16
$ python3 prog.py 4 -v 3
16
Все они выглядят хорошо, кроме последнего, который раскрывает ошибку в нашей
программе. Давайте исправим это, ограничив значения, которые может принять
параметр --verbosity
:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
parser.add_argument("-v", "--verbosity", type=int, choices=[0, 1, 2],
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
print("the square of {} equals {}".format(args.square, answer))
elif args.verbosity == 1:
print("{}^2 == {}".format(args.square, answer))
else:
print(answer)
И вывод:
$ python3 prog.py 4 -v 3
usage: prog.py [-h] [-v {0,1,2}] square
prog.py: error: argument -v/--verbosity: invalid choice: 3 (choose from 0, 1, 2)
$ python3 prog.py 4 -h
usage: prog.py [-h] [-v {0,1,2}] square
positional arguments:
square display a square of a given number
optional arguments:
-h, --help show this help message and exit
-v {0,1,2}, --verbosity {0,1,2}
increase output verbosity
Обратите внимание, что изменение также отражает обоих в сообщении об ошибке, а также помощи строка.
Теперь давайте использовать другой подход игры с детализацией, что довольно
часто. Он также совпадает с тем, как исполняемый файл CPython обрабатывает
собственный аргумент детализации (проверьте выходные данные python --help
):
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display the square of a given number")
parser.add_argument("-v", "--verbosity", action="count",
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
print("the square of {} equals {}".format(args.square, answer))
elif args.verbosity == 1:
print("{}^2 == {}".format(args.square, answer))
else:
print(answer)
Мы ввели другое действие, «count», чтобы подсчитать количество вхождений конкретных необязательных аргументов:
$ python3 prog.py 4
16
$ python3 prog.py 4 -v
4^2 == 16
$ python3 prog.py 4 -vv
the square of 4 equals 16
$ python3 prog.py 4 --verbosity --verbosity
the square of 4 equals 16
$ python3 prog.py 4 -v 1
usage: prog.py [-h] [-v] square
prog.py: error: unrecognized arguments: 1
$ python3 prog.py 4 -h
usage: prog.py [-h] [-v] square
positional arguments:
square display a square of a given number
optional arguments:
-h, --help show this help message and exit
-v, --verbosity increase output verbosity
$ python3 prog.py 4 -vvv
16
- Да, теперь это больше флаг (похожий на
action="store_true"
) в предыдущей версии нашего сценария. Это должно объяснить жалобу. - Он также ведет себя как «store_true» действие.
- Вот демонстрация того, что дает действие «count.» вероятно, вы уже видели подобное использование.
- И если вы не указываете флаг
-v
, этот флаг считается имеющим значениеNone
. - Как и следовало ожидать, указывая длинную форму флага, мы должны получить тот же вывод.
- К сожалению, наши выходные данные справки не очень информативны в отношении
новой способности, которую приобрел наш сценарий, но это всегда можно исправить,
улучшив документацию для нашего сценария (например, с помощью аргумента
help
ключевой). - Последний вывод раскрывает ошибку в нашей программе.
Давайте исправим это:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
parser.add_argument("-v", "--verbosity", action="count",
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
# bugfix: replace == with >=
if args.verbosity >= 2:
print("the square of {} equals {}".format(args.square, answer))
elif args.verbosity >= 1:
print("{}^2 == {}".format(args.square, answer))
else:
print(answer)
И вот что она дает:
$ python3 prog.py 4 -vvv
the square of 4 equals 16
$ python3 prog.py 4 -vvvv
the square of 4 equals 16
$ python3 prog.py 4
Traceback (most recent call last):
File "prog.py", line 11, in <module>
if args.verbosity >= 2:
TypeError: '>=' not supported between instances of 'NoneType' and 'int'
- Первый выход прошел хорошо, и исправляет ошибку, которая была у нас раньше. То есть мы хотим, чтобы любое значение > = 2 было максимально подробным.
- Третий выход не так хорош.
Давайте исправим эту ошибку:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
parser.add_argument("-v", "--verbosity", action="count", default=0,
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity >= 2:
print("the square of {} equals {}".format(args.square, answer))
elif args.verbosity >= 1:
print("{}^2 == {}".format(args.square, answer))
else:
print(answer)
Мы только что ввели еще одно ключевое слово, default
. Мы установили его на
0
, чтобы сделать его сопоставимым с другими значениями int. Помните,
что по умолчанию, если необязательный аргумент не указан, он получает значение
None
, которое не может сравниваться со значением int (следовательно,
исключение TypeError
).
И:
$ python3 prog.py 4
16
Вы можете зайти довольно далеко с тем, что мы узнали до сих пор, и мы только
поцарапали поверхность. Модуль argparse
очень мощный, и мы рассмотрим его
немного больше, прежде чем мы закончим это учебное пособие.
Становлюсь немного продвинутее¶
Что если бы мы хотели расширить нашу крошечную программу для выполнения других сил, а не только квадратов:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
parser.add_argument("-v", "--verbosity", action="count", default=0)
args = parser.parse_args()
answer = args.x**args.y
if args.verbosity >= 2:
print("{} to the power {} equals {}".format(args.x, args.y, answer))
elif args.verbosity >= 1:
print("{}^{} == {}".format(args.x, args.y, answer))
else:
print(answer)
Вывод:
$ python3 prog.py
usage: prog.py [-h] [-v] x y
prog.py: error: the following arguments are required: x, y
$ python3 prog.py -h
usage: prog.py [-h] [-v] x y
positional arguments:
x the base
y the exponent
optional arguments:
-h, --help show this help message and exit
-v, --verbosity
$ python3 prog.py 4 2 -v
4^2 == 16
Заметьте, что до сих пор мы использовали уровень многословия для change текст, который показан. Следующий пример вместо этого использует уровень многословия, чтобы показать текст more вместо этого:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
parser.add_argument("-v", "--verbosity", action="count", default=0)
args = parser.parse_args()
answer = args.x**args.y
if args.verbosity >= 2:
print("Running '{}'".format(__file__))
if args.verbosity >= 1:
print("{}^{} == ".format(args.x, args.y), end="")
print(answer)
Вывод:
$ python3 prog.py 4 2
16
$ python3 prog.py 4 2 -v
4^2 == 16
$ python3 prog.py 4 2 -vv
Running 'prog.py'
4^2 == 16
Конфликтующие опции¶
Пока мы работаем с двумя методы argparse.ArgumentParser
сущность. Давайте представим
третий, add_mutually_exclusive_group()
. Это позволяет нам указывать варианты, которые конфликтуют
друг с другом. Давайте также изменим остальную часть программы, чтобы новый
функционал имел больше смысла: введем опцию --quiet
, которая будет
противоположна --verbose
:
import argparse
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument("-v", "--verbose", action="store_true")
group.add_argument("-q", "--quiet", action="store_true")
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
args = parser.parse_args()
answer = args.x**args.y
if args.quiet:
print(answer)
elif args.verbose:
print("{} to the power {} equals {}".format(args.x, args.y, answer))
else:
print("{}^{} == {}".format(args.x, args.y, answer))
Наша программа теперь проще, и мы потеряли некоторые функциональные возможности ради демонстрации. В любом случае, вот результат:
$ python3 prog.py 4 2
4^2 == 16
$ python3 prog.py 4 2 -q
16
$ python3 prog.py 4 2 -v
4 to the power 2 equals 16
$ python3 prog.py 4 2 -vq
usage: prog.py [-h] [-v | -q] x y
prog.py: error: argument -q/--quiet: not allowed with argument -v/--verbose
$ python3 prog.py 4 2 -v --quiet
usage: prog.py [-h] [-v | -q] x y
prog.py: error: argument -q/--quiet: not allowed with argument -v/--verbose
Это должно быть легко последовать. Я добавил, что последний выход, так что вы можете видеть вид гибкости вы получаете, то есть смешение длинной формы вариантов с короткой формы.
Прежде чем мы завершим, вы, вероятно, хотите сообщить своим пользователям основную цель вашей программы, на всякий случай, если они не знают:
import argparse
parser = argparse.ArgumentParser(description="calculate X to the power of Y")
group = parser.add_mutually_exclusive_group()
group.add_argument("-v", "--verbose", action="store_true")
group.add_argument("-q", "--quiet", action="store_true")
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
args = parser.parse_args()
answer = args.x**args.y
if args.quiet:
print(answer)
elif args.verbose:
print("{} to the power {} equals {}".format(args.x, args.y, answer))
else:
print("{}^{} == {}".format(args.x, args.y, answer))
Обратите внимание на небольшое различие в тексте использования. Обратите
внимание на [-v | -q]
, который говорит нам, что мы можем использовать
-v
или -q
, но не оба одновременно:
$ python3 prog.py --help
usage: prog.py [-h] [-v | -q] x y
calculate X to the power of Y
positional arguments:
x the base
y the exponent
optional arguments:
-h, --help show this help message and exit
-v, --verbose
-q, --quiet