Руководство по Argparse
- author
-
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
Вот что происходит:
- Запуск скрипта без каких-либо опций не отображает ничего на стандартном выходе. Не очень полезно.
- Второй пример начинает демонстрировать полезность модуля
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")
И вот результат:
$ 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, чтобы сделать его сопоставимым с другими целочисленными значениями. Помните, что по умолчанию, если необязательный аргумент не указан, он получает значение None, и это значение нельзя сравнивать с целочисленным значением (отсюда и исключение 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
Обратите внимание, что до сих пор мы использовали уровень детализации для изменения отображаемого текста. В следующем примере уровень детализации используется для отображения большего количества текста:
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
Заключение
Модуль argparse предлагает намного больше, чем показано здесь. Его документация довольно подробна и исчерпывающая, и полна примеров. Пройдя этот учебник, вы должны легко их усвоить, не чувствуя себя перегруженными.
© 2001–2022 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.8/howto/argparse.html