2014-12-17 3 views
16

Я новичок в python и сейчас играю с ним. У меня есть скрипт, который выполняет некоторые вызовы API для устройства. Я хотел бы расширить функциональность и вызвать различные функции, основанные на аргументах, предоставляемых при вызове сценария.Функция вызова на основе argparse

В настоящее время у меня есть следующие:

parser = argparse.ArgumentParser() 
parser.add_argument("--showtop20", help="list top 20 by app", 
        action="store_true") 
parser.add_argument("--listapps", help="list all available apps", 
        action="store_true") 
args = parser.parse_args() 

У меня также есть

def showtop20(): 
    ..... 

и

def listapps(): 
.... 

Как я могу вызвать функцию (и только) на основе аргумент дан? Я не хочу, чтобы запустить

if args.showtop20: 
    #code here 

if args.listapps: 
    #code here 

, как я хочу, чтобы переместить различные функции к модулю позже на сохранении основной исполняемый файл в чистоте и порядке.

+0

Вы могли бы иметь словарь аргументы отображения на функцию '{«showtop20»: showtop20, ...}' - вы затем обновить словарь, если функции перемещены/переименованы. – jonrsharpe

+1

Вместо этого я использую 'store_const', с пустой функцией по умолчанию, а затем вызывать все функции по очереди – njzk2

+0

https://docs.python.org/3/library/argparse.html#sub-commands - проверьте пример, который использует 'add_subparsers' и' set_defaults', чтобы связать команду и функцию. – hpaulj

ответ

14

Так как кажется, что вы хотите запустить один и только один, функция в зависимости от заданных параметров, я хотел бы предложить вам использовать обязательный позиционный аргумент ./prog command, вместо необязательных аргументов (./prog --command1 или ./prog --command2).

так, что-то вроде этого следует сделать это:

FUNCTION_MAP = {'top20' : my_top20_func, 
       'listapps' : my_listapps_func } 

parser.add_argument('command', choices=FUNCTION_MAP.keys()) 

args = parser.parse_args() 

func = FUNCTION_MAP[args.command] 
func() 
+1

лучше определить 'FUNCTION_MAP' сначала и иметь' choice = list (FUNCTION_MAP) ', чтобы сохранить его сухим – GP89

+1

Отличная идея! Я изменил свой код. Хотя я предпочитаю использовать 'MAP.keys()' вместо 'list (MAP)', потому что я думаю, что это более очевидно. –

+0

'choice' отлично работает со словарем. Он автоматически использует свои ключи. – hpaulj

3

Если ваши функции «достаточно просто» взять adventage из type параметра https://docs.python.org/2.7/library/argparse.html#type

типа = может принимать любое отзывной, который принимает один строковый аргумент и возвращает преобразованное значение:

В ваш пример (даже если вам не требуется преобразованное значение):

parser.add_argument("--listapps", help="list all available apps", 
        type=showtop20, 
        action="store") 

Этот простой скрипт:

import argparse 

def showtop20(dummy): 
    print "{0}\n".format(dummy) * 5 

parser = argparse.ArgumentParser() 
parser.add_argument("--listapps", help="list all available apps", 
        type=showtop20, 
        action="store") 
args = parser.parse_args() 

Даст:

# ./test.py --listapps test 
test 
test 
test 
test 
test 
test 
+2

Запустили ли вы этот код? 'store_true' не принимает никаких аргументов, поэтому' showtop20' не должен выполняться. Он будет работать со стандартным действием «хранилище», но я не думаю, что переплетение разбора и исполнения - хорошая идея. – hpaulj

+1

Необходим экстренный «магазин», этот метод является своего рода взломом, но, возможно, самым близким к требованию. – klashxx

+0

Важным отличием вашего подхода является то, что функция выполняется, пока парсер все еще активен. Таким образом, можно последовательно вызвать несколько функций. В некоторых случаях это было бы желательно, в других это могло бы создать головные боли отладки. – hpaulj

7

Есть много способов снятия шкуры этого кота. Вот один, используя action='store_const' (вдохновленный документированной примере subparser):

p=argparse.ArgumentParser() 
p.add_argument('--cmd1', action='store_const', const=lambda:'cmd1', dest='cmd') 
p.add_argument('--cmd2', action='store_const', const=lambda:'cmd2', dest='cmd') 

args = p.parse_args(['--cmd1']) 
# Out[21]: Namespace(cmd=<function <lambda> at 0x9abf994>) 

p.parse_args(['--cmd2']).cmd() 
# Out[19]: 'cmd2' 
p.parse_args(['--cmd1']).cmd() 
# Out[20]: 'cmd1' 

С общим dest, каждое действие ставит свою функцию (const) в том же атрибут пространства имен. Функция вызывается args.cmd().

Как и в примере с документальными подпараметрами, эти функции могут быть записаны так, чтобы использовать другие значения из пространства имен.

args = parse_args() 
args.cmd(args) 

Для сравнения, вот эквивалент subparsers случай:

p = argparse.ArgumentParser() 
sp = p.add_subparsers(dest='cmdstr') 
sp1 = sp.add_parser('cmd1') 
sp1.set_defaults(cmd=lambda:'cmd1') 
sp2 = sp.add_parser('cmd2') 
sp2.set_defaults(cmd=lambda:'cmd2') 

p.parse_args(['cmd1']).cmd() 
# Out[25]: 'cmd1' 

Как показано в документации, subparsers позволяет определить различные аргументы параметров для каждой из команд.

И, конечно, все эти add аргументы или выражения парсера могут быть созданы в цикле над некоторым списком или словарем, который соединяет ключ с функцией.

Еще одно важное соображение - какое использование и помощь вы хотите? Различные подходы генерируют очень разные справочные сообщения.

+0

@hpaulk, я знаю, что это было какое-то время, и это не будет конкретно отвечать на исходный вопрос, но я искал способ передать несколько флагов, где каждый флаг вызывает определенную функцию (а не только одну функцию путем переопределения) dest "). Я нашел действие 'action = 'append_const'', которое делает именно это. Не могли бы вы добавить это к своему ответу? – OozeMeister

+0

Пример: https://gist.github.com/frenchtoast747/cdbdba055b649f44d0f86bc88d29b6b8 – OozeMeister

1

По крайней мере, из того, что вы описали, --showtop20 и --listapps звучат скорее как под-команды, чем опции. Предполагая, что это так, мы можем использовать подпарамеры для достижения желаемого результата. Вот доказательство концепции:

import argparse 
import sys 

def showtop20(): 
    print('running showtop20') 

def listapps(): 
    print('running listapps') 

parser = argparse.ArgumentParser() 
subparsers = parser.add_subparsers() 

# Create a showtop20 subcommand  
parser_showtop20 = subparsers.add_parser('showtop20', help='list top 20 by app') 
parser_showtop20.set_defaults(func=showtop20) 

# Create a listapps subcommand  
parser_listapps = subparsers.add_parser('listapps', help='list all available apps') 
parser_listapps.set_defaults(func=listapps) 

# Print usage message if no args are supplied. 

# NOTE: Python 2 will error 'too few arguments' if no subcommand is supplied. 
#  No such error occurs in Python 3, which makes it feasible to check 
#  whether a subcommand was provided (displaying a help message if not). 
#  argparse internals vary significantly over the major versions, so it's 
#  much easier to just override the args passed to it. 

if len(sys.argv) <= 1: 
    sys.argv.append('--help') 

options = parser.parse_args() 

# Run the appropriate function (in this case showtop20 or listapps) 
options.func() 

# If you add command-line options, consider passing them to the function, 
# e.g. `options.func(options)` 
Смежные вопросы