Содержание страницы
Руководство по Argparse¶Argparse Tutorial
| автор: | Tshepang Lekhonkhobe |
|---|
Это руководство предназначено для плавного введения в argparse, рекомендуемый модуль для разбора командной строки в стандартной библиотеке Python.
Примечание
Существуют два других модуля, выполняющих ту же задачу: getopt (аналог getopt() из языка C) и устаревший optparse. Обратите также внимание, что argparse основан на optparse, и поэтому очень похож в использовании.
Основные понятия¶Concepts
Давайте покажем, какие возможности мы будем изучать в этом вводном руководстве, на примере команды 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 в этом случае называется необязательным аргументом.
- Это фрагмент справки. Она очень полезна: вы можете встретить незнакомую программу и понять, как она работает, просто прочитав её справку.
Основы¶The basics
Начнём с очень простого примера, который (почти) ничего не делает:
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, – единственная опция, которую мы получаем бесплатно (т.е. без необходимости её указывать). Указание чего-либо ещё приводит к ошибке. Но даже в этом случае мы получаем полезное сообщение об использовании, тоже бесплатно.
Знакомство с позиционными аргументами¶Introducing Positional arguments
Пример:
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'
Всё прошло хорошо. Программа теперь даже корректно завершается при неверном вводе перед продолжением.
Знакомство с необязательными аргументами¶Introducing Optional arguments
До сих пор мы игрались с позиционными аргументами. Давайте посмотрим, как добавлять необязательные:
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.
- Программа выдаёт ошибку, если указано значение, в полном соответствии с тем, что такое флаги на самом деле.
- Обратите внимание на другой текст справки.
Короткие опции¶Short options
Если вы знакомы с работой из командной строки, то заметили, что я ещё не касался темы коротких версий опций. Всё довольно просто:
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
Обратите внимание, что новая возможность также отражена в справочном тексте.
Сочетание позиционных и необязательных аргументов¶Combining Positional and Optional arguments
Наша программа становится всё сложнее:
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». Вы, вероятно, уже встречали подобное использование.
- И, как и в случае с действием “store_true”, если не указать флаг -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
# исправление ошибки: заменить == на >=
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: unorderable types: NoneType() >= 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 очень мощный, и мы рассмотрим его чуть подробнее, прежде чем завершить это руководство.
Немного более продвинутый материал¶Getting a little more advanced
Что, если бы мы захотели расширить нашу крошечную программу, чтобы она вычисляла не только квадраты, но и другие степени:
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
Конфликтующие опции¶Conflicting options
До сих пор мы работали с двумя методами экземпляра 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