Учебник по Argparse
- author:
-
Tshepang Mbambo
Этот учебник предназначен для ознакомления с 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()
Результат выполнения кода:
$ 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". Это означает, что если параметр указан, присвоить значениеTrueargs.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")
И вот результат:
$ 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–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.13/howto/argparse.html