Руководство по 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] options: -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 options: -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 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)
Вот результат выполнения кода:
$ 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]
options:
-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] options: -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] 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)
И теперь вывод:
$ 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(f"the square of {args.square} equals {answer}")
elif args.verbosity == 1:
print(f"{args.square}^2 == {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(f"the square of {args.square} equals {answer}")
elif args.verbosity == 1:
print(f"{args.square}^2 == {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
options:
-h, --help show this help message and exit
-v {0,1,2}, --verbosity {0,1,2}
increase output verbosity
Обратите внимание, что изменение отражено как в сообщении об ошибке, так и в строке справки.
Теперь давайте воспользуемся другим подходом к работе с уровнем подробности, который довольно распространён. Он также соответствует тому, как исполняемый файл CPython обрабатывает свой собственный аргумент verbose (проверьте вывод 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», для подсчёта количества вхождений определённых опций.
$ 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 options: -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(f"the square of {args.square} equals {answer}")
elif args.verbosity >= 1:
print(f"{args.square}^2 == {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(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).
И:
$ 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(f"{args.x} to the power {args.y} equals {answer}")
elif args.verbosity >= 1:
print(f"{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 options: -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(f"Running '{__file__}'")
if args.verbosity >= 1:
print(f"{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(f"{args.x} to the power {args.y} equals {answer}")
else:
print(f"{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(f"{args.x} to the power {args.y} equals {answer}")
else:
print(f"{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 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 предлагает гораздо больше, чем показано здесь. Его документация довольно подробная и полная, а также содержит много примеров. Пройдя этот учебник, вы легко сможете их освоить, не чувствуя себя перегруженным.
© 2001–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.11/howto/argparse.html