Учебное руководство по argparse
- автор:
-
Tshepang Mbambo
Это руководство представляет собой доступное введение в argparse — рекомендуемый модуль стандартной библиотеки Python для разбора командной строки.
Примечание
Стандартная библиотека включает ещё две библиотеки, непосредственно связанные с обработкой параметров командной строки: низкоуровневый модуль optparse (для настройки которого под конкретное приложение может потребоваться больше кода, но он также позволяет приложению запрашивать поведение, которое не поддерживается модулем argparse), а также очень низкоуровневый модуль getopt (который служит аналогом семейства функций getopt(), доступных программистам на C). Хотя в этом руководстве непосредственно не рассматривается ни один из этих модулей, многие основные концепции argparse изначально появились в optparse, поэтому некоторые части этого руководства будут полезны и пользователям 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()
Ниже показан результат выполнения кода:
$ python prog.py $ python prog.py --help usage: prog.py [-h] options: -h, --help show this help message and exit $ python prog.py --verbose usage: prog.py [-h] prog.py: error: unrecognized arguments: --verbose $ python 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)
Запустим код:
$ python prog.py usage: prog.py [-h] echo prog.py: error: the following arguments are required: echo $ python prog.py --help usage: prog.py [-h] echo positional arguments: echo options: -h, --help show this help message and exit $ python 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)
Получим:
$ python prog.py -h usage: prog.py [-h] echo positional arguments: echo echo the string you use here options: -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)
Ниже показан результат выполнения кода:
$ python 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)
Ниже показан результат выполнения кода:
$ python prog.py 4 16 $ python 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")
Результат:
$ python prog.py --verbosity 1
verbosity turned on
$ python prog.py
$ python prog.py --help
usage: prog.py [-h] [--verbosity VERBOSITY]
options:
-h, --help show this help message and exit
--verbosity VERBOSITY
increase output verbosity
$ python 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")
Результат:
$ python prog.py --verbose verbosity turned on $ python prog.py --verbose 1 usage: prog.py [-h] [--verbose] prog.py: error: unrecognized arguments: 1 $ python prog.py --help usage: prog.py [-h] [--verbose] options: -h, --help show this help message and exit --verbose increase output verbosity
Вот что происходит:
- Теперь параметр больше похож на флаг, чем на параметр, требующий значения. Мы даже изменили имя параметра, чтобы оно соответствовало этой идее. Обратите внимание: теперь мы указываем новый именованный параметр
actionи присваиваем ему значение"store_true". Это означает, что при указании параметра переменнойargs.verboseприсваивается значениеTrue. Если параметр не указан, используется значение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")
Вот результат:
$ python prog.py -v verbosity turned on $ python prog.py --help usage: prog.py [-h] [-v] options: -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(f"the square of {args.square} equals {answer}")
else:
print(answer)
Теперь результат:
$ python prog.py usage: prog.py [-h] [-v] square prog.py: error: the following arguments are required: square $ python prog.py 4 16 $ python prog.py 4 --verbose the square of 4 equals 16 $ python 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(f"the square of {args.square} equals {answer}")
elif args.verbosity == 1:
print(f"{args.square}^2 == {answer}")
else:
print(answer)
Результат:
$ python prog.py 4 16 $ python prog.py 4 -v usage: prog.py [-h] [-v VERBOSITY] square prog.py: error: argument -v/--verbosity: expected one argument $ python prog.py 4 -v 1 4^2 == 16 $ python prog.py 4 -v 2 the square of 4 equals 16 $ python 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(f"the square of {args.square} equals {answer}")
elif args.verbosity == 1:
print(f"{args.square}^2 == {answer}")
else:
print(answer)
Результат:
$ python 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)
$ python prog.py 4 -h
usage: prog.py [-h] [-v {0,1,2}] square
positional arguments:
square display a square of a given number
options:
-h, --help show this help message and exit
-v, --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(f"the square of {args.square} equals {answer}")
elif args.verbosity == 1:
print(f"{args.square}^2 == {answer}")
else:
print(answer)
Мы добавили ещё одно действие — «count», которое подсчитывает количество повторений определённых параметров.
$ python prog.py 4 16 $ python prog.py 4 -v 4^2 == 16 $ python prog.py 4 -vv the square of 4 equals 16 $ python prog.py 4 --verbosity --verbosity the square of 4 equals 16 $ python prog.py 4 -v 1 usage: prog.py [-h] [-v] square prog.py: error: unrecognized arguments: 1 $ python prog.py 4 -h usage: prog.py [-h] [-v] square positional arguments: square display a square of a given number options: -h, --help show this help message and exit -v, --verbosity increase output verbosity $ python 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(f"the square of {args.square} equals {answer}")
elif args.verbosity >= 1:
print(f"{args.square}^2 == {answer}")
else:
print(answer)
Вот что получим:
$ python prog.py 4 -vvv
the square of 4 equals 16
$ python prog.py 4 -vvvv
the square of 4 equals 16
$ python 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(f"the square of {args.square} equals {answer}")
elif args.verbosity >= 1:
print(f"{args.square}^2 == {answer}")
else:
print(answer)
Мы добавили ещё один именованный аргумент — default. Ему присвоено значение 0, чтобы его можно было сравнивать с другими целочисленными значениями. Помните, что по умолчанию необязательному аргументу, если он не указан, присваивается значение None, которое нельзя сравнить с целым числом (отсюда и исключение TypeError).
И результат:
$ python 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(f"{args.x} to the power {args.y} equals {answer}")
elif args.verbosity >= 1:
print(f"{args.x}^{args.y} == {answer}")
else:
print(answer)
Результат:
$ python prog.py usage: prog.py [-h] [-v] x y prog.py: error: the following arguments are required: x, y $ python prog.py -h usage: prog.py [-h] [-v] x y positional arguments: x the base y the exponent options: -h, --help show this help message and exit -v, --verbosity $ python 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(f"Running '{__file__}'")
if args.verbosity >= 1:
print(f"{args.x}^{args.y} == ", end="")
print(answer)
Результат:
$ python prog.py 4 2 16 $ python prog.py 4 2 -v 4^2 == 16 $ python prog.py 4 2 -vv Running 'prog.py' 4^2 == 16
Указание неоднозначных аргументов
Если неясно, является ли аргумент позиционным или предназначен для параметра, можно использовать --, чтобы сообщить методу parse_args(), что всё после него является позиционным аргументом:
>>> parser = argparse.ArgumentParser(prog='PROG')
>>> parser.add_argument('-n', nargs='+')
>>> parser.add_argument('args', nargs='*')
>>> # ambiguous, so parse_args assumes it's an option
>>> parser.parse_args(['-f'])
usage: PROG [-h] [-n N [N ...]] [args ...]
PROG: error: unrecognized arguments: -f
>>> parser.parse_args(['--', '-f'])
Namespace(args=['-f'], n=None)
>>> # ambiguous, so the -n option greedily accepts arguments
>>> parser.parse_args(['-n', '1', '2', '3'])
Namespace(args=[], n=['1', '2', '3'])
>>> parser.parse_args(['-n', '1', '--', '2', '3'])
Namespace(args=['2', '3'], n=['1'])
Конфликтующие параметры
До сих пор мы использовали два метода экземпляра 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(f"{args.x} to the power {args.y} equals {answer}")
else:
print(f"{args.x}^{args.y} == {answer}")
Теперь наша программа стала проще, а ради демонстрации мы отказались от части её возможностей. В любом случае, вот результат:
$ python prog.py 4 2 4^2 == 16 $ python prog.py 4 2 -q 16 $ python prog.py 4 2 -v 4 to the power 2 equals 16 $ python 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 $ python 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(f"{args.x} to the power {args.y} equals {answer}")
else:
print(f"{args.x}^{args.y} == {answer}")
Обратите внимание на небольшое изменение текста использования. Обратите внимание на [-v | -q]: оно сообщает, что можно использовать либо -v, либо -q, но не оба одновременно:
$ python 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 options: -h, --help show this help message and exit -v, --verbose -q, --quiet
Как перевести вывод argparse
Вывод модуля argparse, включая справочный текст и сообщения об ошибках, подготовлен для перевода с помощью модуля gettext. Это позволяет приложениям легко локализовать сообщения, создаваемые модулем argparse. См. также раздел Интернационализация программ и модулей.
Например, в этом выводе модуля argparse:
$ python 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 options: -h, --help show this help message and exit -v, --verbose -q, --quiet
Строки usage:, positional arguments:, options: и show this help message and exit можно перевести.
Чтобы перевести эти строки, сначала необходимо извлечь их в файл .po. Например, с помощью Babel выполните эту команду:
$ pybabel extract -o messages.po /usr/lib/python3.12/argparse.py
Эта команда извлечёт все строки, предназначенные для перевода, из модуля argparse и запишет их в файл с именем messages.po. Предполагается, что Python установлен в каталоге /usr/lib.
Узнать расположение модуля argparse в вашей системе можно с помощью этого скрипта:
import argparse print(argparse.__file__)
После того как сообщения в файле .po будут переведены, а переводы установлены с помощью gettext, модуль argparse сможет отображать переведённые сообщения.
Чтобы переводить собственные строки в выводе модуля argparse, используйте gettext.
Пользовательские преобразователи типов
Модуль argparse позволяет задавать пользовательские преобразователи типов для аргументов командной строки. С их помощью можно изменять ввод пользователя перед сохранением в argparse.Namespace. Это может быть полезно, если перед использованием ввода в программе его нужно предварительно обработать.
Для пользовательского преобразователя типа можно использовать любой вызываемый объект, который принимает один строковый аргумент (значение аргумента) и возвращает преобразованное значение. Однако, если требуется обрабатывать более сложные сценарии, вместо этого можно использовать пользовательский класс действия с параметром action.
Например, предположим, что нужно обрабатывать аргументы с разными префиксами соответствующим образом:
import argparse
parser = argparse.ArgumentParser(prefix_chars='-+')
parser.add_argument('-a', metavar='<value>', action='append',
type=lambda x: ('-', x))
parser.add_argument('+a', metavar='<value>', action='append',
type=lambda x: ('+', x))
args = parser.parse_args()
print(args)
Результат:
$ python prog.py -a value1 +a value2
Namespace(a=[('-', 'value1'), ('+', 'value2')])
В этом примере мы:
- Создали анализатор с пользовательскими символами префикса, используя параметр
prefix_chars. - Определили два аргумента —
-aи+a, — для которых использовали параметрtype, чтобы создать пользовательские преобразователи типов и сохранить значение в кортеже вместе с префиксом.
Без пользовательских преобразователей типов аргументы -a и +a обрабатывались бы как один и тот же аргумент, что было бы нежелательно. Пользовательские преобразователи типов позволили нам различать эти два аргумента.
Заключение
Модуль argparse предлагает гораздо больше возможностей, чем описано здесь. Его документация подробна и исчерпывающа, а также содержит множество примеров. После изучения этого руководства вам будет легко разобраться в ней, не чувствуя себя перегруженными.
© 2001 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.14/howto/argparse.html