Приклад простого аргументації: 1 аргумент, 3 результати


529

Документація для argparse модуля пітона , в той час як відмінно я впевнений, це занадто багато для мене крихітний початківця мозку , щоб зрозуміти прямо зараз. Мені не потрібно займатися математикою в командному рядку або втручатися в рядки форматування на екрані або змінювати параметри символів. Все, що я хочу зробити, це "Якщо аргумент - A, зробіть це, якщо B зробіть це, якщо жодне з перерахованого вище не допоможе і кине" .


15
то просто перевірте sys.argvпотрібний аргумент ...
JBernardo

10
Коли-небудь пробував plac ? Це проста у використанні обгортка над аргументацією з чудовою документацією .
kirbyfan64sos

157
це не ти. це аргумент. це намагається перевезти вас у подорож до зірок і не байдуже, куди ви прямували.
Флоріан Хейгл

11
Ще раз божевільні «пітонічні» API: /
mlvljr

69
Благословіть вас, матовий вілкі, що стоїть скрізь крихітний мозок для початківців.
полька

Відповіді:


255

Моє розуміння оригінального питання двояке. По-перше, з точки зору найпростішого можливого аргументаційного прикладу, я здивований, що я цього не бачив. Звичайно, щоб бути мертвим просто, це також все накладні витрати з невеликою потужністю, але це може вас почати.

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("a")
args = parser.parse_args()

if args.a == 'magic.name':
    print 'You nailed it!'

Але цей позиційний аргумент зараз необхідний. Якщо ви не залишите його під час виклику цієї програми, ви отримаєте помилку щодо відсутніх аргументів. Це підводить мене до другої частини оригінального питання. Мет Вілкі, схоже, хоче єдиний необов'язковий аргумент без названої мітки (мітки - варіантів). Моя пропозиція полягає в тому, щоб змінити код вище таким чином:

...
parser.add_argument("a", nargs='?', default="check_string_for_empty")
...
if args.a == 'check_string_for_empty':
    print 'I can tell that no argument was given and I can deal with that here.'
elif args.a == 'magic.name':
    print 'You nailed it!'
else:
    print args.a

Можливо, може бути і більш елегантне рішення, але це працює і є мінімалістичним.


4
Через деякий час, замислюючись, я роблю висновок, що це питання насправді найкраще відповідає на запитання Q і про загрозу, у якій я був у той час. Інші чудові відповіді зібрали більш ніж достатньо представників, щоб довести свою цінність і можуть витримати невелику конкуренцію. :-)
matt wilkie

@badnack: Це все, що ти хочеш, як би це не було. Якщо ви очікуєте одного аргументу, наприклад, ім'я файлу, це те, що було введено як ім'я файлу в командному рядку. Потім ви можете зробити власну обробку, щоб визначити, чи існує вона у файловій системі, але це ще одне запитання.
Могутній

363

Ось як я це роблю argparse(з кількома аргами):

parser = argparse.ArgumentParser(description='Description of your program')
parser.add_argument('-f','--foo', help='Description for foo argument', required=True)
parser.add_argument('-b','--bar', help='Description for bar argument', required=True)
args = vars(parser.parse_args())

args буде словник, що містить аргументи:

if args['foo'] == 'Hello':
    # code here

if args['bar'] == 'World':
    # code here

У вашому випадку просто додайте лише один аргумент.


3
як згадувалося в моєму коментарі до іншої відповіді, я хотів би зберегти автоматичне довідковане форматування argparse, але, здається, не існує можливості мати аргумент без повідомлення, (швидше за все, я просто не розумію цього, коли бачу його ), наприклад, потрібно зробити foo.py --action installабо foo.py --action removeзамість цього простоfoo.py install
matt wilkie

7
@mattwilkie Тоді вам слід визначити позиційний аргумент на зразок цього: parser.add_argument('install', help='Install the app') (Зверніть увагу, ви не можете визначити позиційний аргумент required=True)
Дієго Наварро

32
Як ноуб для аргументації, ця відповідь справді допомогла, тому що я не знав, де знайти варіанти після їх передачі . Іншими словами, мені потрібно було зрозуміти, як argsстворювався дикт, як зазначено вище.
mrKelley

3
Використовуйте "коротку форму" для виклику програми безпосередньо з командного рядка та "довгу форму", коли ви запускаєте програму / команду в сценарії. У такому випадку легше читати з довгою формою і, таким чином, легше слідувати логіці коду / сценарію.
ola

17
Особисто я вважаю більш чистим доступ до аргументів як args.fooі args.barзамість синтаксису словника. У будь-якому випадку це добре, звичайно, але аргументи - це насправді не словник, а argparse.Namespaceоб’єкт.
Майкл Міор

210

argparseДокументація досить добре , але залишає кілька корисних деталей , які не можуть бути очевидні. (@Diego Navarro вже згадував про це, але я спробую трохи розширити його відповідь.) Основне використання полягає в наступному:

parser = argparse.ArgumentParser()
parser.add_argument('-f', '--my-foo', default='foobar')
parser.add_argument('-b', '--bar-value', default=3.14)
args = parser.parse_args()

Об'єкт, з якого ви повертаєтесь, parse_args()- це об’єкт «Простір імен»: Об’єкт, змінні якого члена названі за аргументами командного рядка. NamespaceОб'єкт як отримати доступ до аргументів і значенням , пов'язане з ними:

args = parser.parse_args()
print args.my_foo
print args.bar_value

(Зверніть увагу, що argparseзамінює "-" у назвах вашого аргументу підкреслення під час іменування змінних.)

У багатьох ситуаціях ви можете використовувати аргументи просто як прапори, які не мають значення. Ви можете додати такі в аргументації так:

parser.add_argument('--foo', action='store_true')
parser.add_argument('--no-foo', action='store_false')

Вищезгадане створить змінні з назвою 'foo' зі значенням True та 'no_foo' зі значенням False відповідно:

if (args.foo):
    print "foo is true"

if (args.no_foo is False):
    print "nofoo is false"

Зауважте також, що ви можете використовувати "необхідний" варіант при додаванні аргументу:

parser.add_argument('-o', '--output', required=True)

Таким чином, якщо ви опустите цей аргумент у командному рядку, argparseви скажете, що він відсутній, і зупинить виконання вашого сценарію.

Нарешті, зауважте, що можна створити структуру dict своїх аргументів за допомогою varsфункції, якщо це полегшує вам життя.

args = parser.parse_args()
argsdict = vars(args)
print argsdict['my_foo']
print argsdict['bar_value']

Як бачите, varsповертає дикт із назвами ваших аргументів як ключі та їх значеннями як, er, значення.

Ви можете зробити багато інших варіантів і дій, але це повинно охоплювати найважливіші, загальні сценарії використання.


3
Який сенс '-f'і '-b'? Чому ти не можеш цього пропустити?
користувач2763361

13
Для кожного варіанту виконання досить звичайно мати версію "короткої форми" (один тире) і "довгої форми" (два тире). Ви побачите це, наприклад, майже в кожній стандартній утиліті Unix / Linux; зробіть man cpабо, man lsі ви побачите, що багато варіантів є в обох смаках (наприклад -f, --force). Напевно, існує велика різниця з причин, чому люди віддають перевагу тому чи іншому, але в будь-якому випадку досить стандартно зробити обидві форми доступними у вашій програмі.
DMH

59

Метт запитує про позиційні параметри в argparse, і я погоджуюся, що документації Python не вистачає в цьому аспекті. На ~ 20 непарних сторінках немає жодного повного прикладу, який показує як розбір, так і використання позиційних параметрів .

Жоден з інших відповідей тут не містить повного прикладу позиційних параметрів, тому ось повний приклад:

# tested with python 2.7.1
import argparse

parser = argparse.ArgumentParser(description="An argparse example")

parser.add_argument('action', help='The action to take (e.g. install, remove, etc.)')
parser.add_argument('foo-bar', help='Hyphens are cumbersome in positional arguments')

args = parser.parse_args()

if args.action == "install":
    print("You asked for installation")
else:
    print("You asked for something other than installation")

# The following do not work:
# print(args.foo-bar)
# print(args.foo_bar)

# But this works:
print(getattr(args, 'foo-bar'))

Що мене відкинуло, це те, що argparse перетворить названий аргумент "--foo-bar" у "foo_bar", але позиційний параметр під назвою "foo-bar" залишається "foo-bar", що робить його менш очевидним, як зробити використовувати його у своїй програмі.

Зауважте два рядки наприкінці мого прикладу - жоден із них не буде працювати, щоб отримати значення позиційного параму нижнього колони. Перший, очевидно, помиляється (це арифметичний вираз args.foo мінус бар), але другий також не працює:

AttributeError: 'Namespace' object has no attribute 'foo_bar'

Якщо ви хочете використовувати foo-barатрибут, ви повинні використовувати getattr, як це видно в останньому рядку мого прикладу. Що божевільне - це те, що якщо ви спробували dest=foo_barзмінити ім’я власності на щось легше отримати доступ, ви отримаєте дійсно химерне повідомлення про помилку:

ValueError: dest supplied twice for positional argument

Ось як працює приклад вище:

$ python test.py
usage: test.py [-h] action foo-bar
test.py: error: too few arguments

$ python test.py -h
usage: test.py [-h] action foo-bar

An argparse example

positional arguments:
  action      The action to take (e.g. install, remove, etc.)
  foo-bar     Hyphens are cumbersome in positional arguments

optional arguments:
  -h, --help  show this help message and exit

$ python test.py install foo
You asked for installation
foo

5
nargs='?'є заклинанням для «додаткового позиційного» згідно stackoverflow.com/questions/4480075 / ...
MarkHu

Про те, що позиція foo-barне перетворена foo_bar, розглядається в bugs.python.org/issue15125 .
hpaulj

2
Я думаю, що простіше вирішити цю помилку - просто викликати аргумент "foo_bar" замість "foo-bar", а потім print args.foo_barпрацює. Оскільки це позиційний аргумент, вам не потрібно вказувати ім'я під час виклику сценарію, тому це не має значення для користувача.
луатор

@luator Ви маєте рацію, легко перейменувати аргумент, але автор звіту про помилки робить хороший випадок, що це все-таки неправильна поведінка через непотрібне когнітивне навантаження. Під час використання argparse потрібно призупинити та згадати різні умови іменування для параметрів та аргументів. Див. Bugs.python.org/msg164968 .
Марк Е. Хааз

1
@mehaase Я повністю погоджуюся, що це невдача, яку слід виправити. Я просто думаю, що перейменування аргументу - це простіший і менш заплутаний спосіб вирішення, ніж використання getattr(він також більш гнучкий, оскільки дозволяє змінювати аргумент з необов'язкового на позиційний, не змінюючи код, який використовує значення).
луатор

22

Ще один короткий вступ, натхненний цією публікацією .

import argparse

# define functions, classes, etc.

# executes when your script is called from the command-line
if __name__ == "__main__":

    parser = argparse.ArgumentParser()
    #
    # define each option with: parser.add_argument
    #
    args = parser.parse_args() # automatically looks at sys.argv
    #
    # access results with: args.argumentName
    #

Аргументи визначаються комбінаціями з наступного:

parser.add_argument( 'name', options... )              # positional argument
parser.add_argument( '-x', options... )                # single-char flag
parser.add_argument( '-x', '--long-name', options... ) # flag with long name

Поширені варіанти:

  • довідка : опис цього аргументу при --helpвикористанні.
  • default : значення за замовчуванням, якщо аргумент опущено.
  • тип : якщо ви очікуєте, що є ( floatабо intє інакше str).
  • dest : надати прапор іншої назви (наприклад '-x', '--long-name', dest='longName').
    Примітка: за замовчуванням--long-name доступ доargs.long_name
  • дія : для спеціального опрацювання певних аргументів
    • store_true, store_false: для булевих арг
      '--foo', action='store_true' => args.foo == True
    • store_const: використовувати з опцієюconst
      '--foo', action='store_const', const=42 => args.foo == 42
    • count: для повторних варіантів, як у./myscript.py -vv
      '-v', action='count' => args.v == 2
    • append: для повторних варіантів, як у./myscript.py --foo 1 --foo 2
      '--foo', action='append' => args.foo == ['1', '2']
  • вимагається : якщо потрібний прапор або позитивний аргумент - ні.
  • nargs : для прапора для захоплення N арг
    ./myscript.py --foo a b => args.foo = ['a', 'b']
  • вибір : обмежити можливі входи (вказати як список рядків чи вбудов, якщо type=int).

12

Зверніть увагу на навчальний посібник Argparse в Python HOWTO . Він починається з більшості основних прикладів, як цей:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
                    help="display a square of a given number")
args = parser.parse_args()
print(args.square**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", type=int, choices=[0, 1, 2],
                    help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
    print("the square of {} equals {}".format(args.square, answer))
elif args.verbosity == 1:
    print("{}^2 == {}".format(args.square, answer))
else:
    print(answer)

Приємно бачити, що документи були оновлені. Я запевняю, що це було не так, коли ОП розмістило це питання 5 років тому.
ntwrkguru

10

Ось що я придумав у своєму навчальному проекті завдяки головним чином @DMH ...

Демо-код:

import argparse

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('-f', '--flag', action='store_true', default=False)  # can 'store_false' for no-xxx flags
    parser.add_argument('-r', '--reqd', required=True)
    parser.add_argument('-o', '--opt', default='fallback')
    parser.add_argument('arg', nargs='*') # use '+' for 1 or more args (instead of 0 or more)
    parsed = parser.parse_args()
    # NOTE: args with '-' have it replaced with '_'
    print('Result:',  vars(parsed))
    print('parsed.reqd:', parsed.reqd)

if __name__ == "__main__":
    main()

Це може розвинутися і доступне в Інтернеті: command-line.py

Сценарій, щоб дати цьому коду тренування: command-line-demo.sh


2
Нарешті, аргументаційний приклад, який має сенс
opentokix

5

Ви також можете використовувати plac (обгортку навколо argparse).

Як бонус він створює акуратні довідкові інструкції - див. Нижче.

Приклад сценарію:

#!/usr/bin/env python3
def main(
    arg: ('Argument with two possible values', 'positional', None, None, ['A', 'B'])
):
    """General help for application"""
    if arg == 'A':
        print("Argument has value A")
    elif arg == 'B':
        print("Argument has value B")

if __name__ == '__main__':
    import plac
    plac.call(main)

Приклад виводу:

Аргументів не надано - example.py :

usage: example.py [-h] {A,B}
example.py: error: the following arguments are required: arg

Несподіваний аргумент надано - example.py C :

usage: example.py [-h] {A,B}
example.py: error: argument arg: invalid choice: 'C' (choose from 'A', 'B')

Надано правильний аргумент - example.py A :

Argument has value A

Повне меню довідки (генерується автоматично) - example.py -h :

usage: example.py [-h] {A,B}

General help for application

positional arguments:
  {A,B}       Argument with two possible values

optional arguments:
  -h, --help  show this help message and exit

Коротке пояснення:

Ім'я аргументу зазвичай дорівнює імені параметра ( arg).

Анотація кортежу після argпараметра має таке значення:

  • Опис ( Argument with two possible values)
  • Тип аргументу - один із "прапор", "опція" або "позиційний" ( positional)
  • Скорочення ( None)
  • Тип значення аргументу - наприклад. float, string ( None)
  • Обмежений набір варіантів ( ['A', 'B'])

Документація:

Щоб дізнатися більше про використання plac, ознайомтеся з його чудовою документацією:

Plac: Розбір командного рядка простим шляхом


4

Щоб додати те, що заявили інші:

Зазвичай я люблю використовувати параметр 'dest', щоб вказати ім'я змінної, а потім використовувати 'globals (). Update ()', щоб розмістити ці змінні у глобальному просторі імен.

Використання:

$ python script.py -i "Hello, World!"

Код:

...
parser.add_argument('-i', '--input', ..., dest='inputted_variable',...)
globals().update(vars(parser.parse_args()))
...
print(inputted_variable) # Prints "Hello, World!"

Внутрішньо argparseвикористовує getattrта setattrотримує доступ до значень у просторі імен. Таким чином, це не турбує дивно сформовані destзначення.
hpaulj

1

Дійсно простий спосіб використовувати argparse та змінити перемикачі '-h' / '--help', щоб відобразити власні довідкові інструкції з особистого коду, - це встановити довідку за замовчуванням на False, ви також можете додати стільки додаткових .add_argument, скільки вам потрібно. :

import argparse

parser = argparse.ArgumentParser(add_help=False)

parser.add_argument('-h', '--help', action='help',
                help='To run this script please provide two arguments')
parser.parse_args()

Виконати: python test.py -h

Вихід:

usage: test.py [-h]

optional arguments:
  -h, --help  To run this script please provide two arguments

-1

Найпростіша відповідь!

PS той, хто написав документ аргументації, нерозумний

код пітона:

import argparse
parser = argparse.ArgumentParser(description='')
parser.add_argument('--o_dct_fname',type=str)
parser.add_argument('--tp',type=str)
parser.add_argument('--new_res_set',type=int)
args = parser.parse_args()
o_dct_fname = args.o_dct_fname
tp = args.tp
new_res_set = args.new_res_set

запущений код

python produce_result.py --o_dct_fname o_dct --tp father_child --new_res_set 1

Ця відповідь не додає нічого нового / іншого, ніж існуючі відповіді.
NVS Abhilash

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.