Функция вызова на основе argparse

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

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

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

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

Ответы

Ответ 1

Так как кажется, что вы хотите запустить одну и только одну функцию в зависимости от приведенных аргументов, я бы предложил вам использовать обязательный позиционный аргумент ./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()

Ответ 2

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

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)

Для сравнения, здесь эквивалентный случай подпарщиков:

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'

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

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

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

Ответ 3

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

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

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

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

Этот простой script:

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

Ответ 4

По крайней мере, из того, что вы описали, --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)`