Spec-Zone.ru › Python 3.12

Учебник по 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". Это означает, что если параметр указан, присваиваем значение 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")

И вот он:

$ 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 {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(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 предлагает гораздо больше, чем показано здесь. Его документация довольно подробная и исчерпывающая, полная примеров. Пройдя этот учебник, вы должны без труда усвоить их, не чувствуя перегрузки.

© 2001–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.12/howto/argparse.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API