使用 argparse 输出调用函数
Posted
技术标签:
【中文标题】使用 argparse 输出调用函数【英文标题】:Using the argparse output to call functions 【发布时间】:2011-03-28 03:56:11 【问题描述】:目前我的代码如下所示。它允许我解析我的程序脚本获取的多个参数。是否有更接近“最佳实践”的不同方式?我还没有看到实际使用argparse
输出的代码,只知道如何设置它。
def useArguments():
x = 0
while x <= 5:
if x == 0:
if args.getweather != None:
getWeather(args.getweather)
if x == 1:
if args.post != None:
post(args.post)
if x == 2:
if args.custompost != None:
custompost(args.custompost)
if x == 3:
if args.list != None:
listAccounts(args.list)
if x == 4:
if args.add != None:
addAccount(args.add[0])
if x == 5:
if args.edit != None:
editAccount(args.edit[0])
x = x + 1
if __name__ == '__main__':
updateConfig()
parser = argparse.ArgumentParser(description='Post Yahoo weather to Twitter.', epilog="Report any bugs to example@email.com", prog='Program')
parser.add_argument('-a', '--add', nargs=1, help='Add a new account. Use the desired account name as an argument.')
parser.add_argument('-e', '--edit', nargs=1, choices=accountListSTR[:-1], help='Edit an account. Use the desired account name as an argument.')
parser.add_argument('-g', '--getweather', nargs='*', choices=accountListSTR, help='Get weather and post here. Specify account(s) as argument. Use "all" for all accounts. If you specify multiple accounts, separate by a space NOT a comma.')
parser.add_argument('-p', '--post', nargs='*', choices=accountListSTR, help='Post weather to Twitter. Specify account(s) as argument. Use "all" for all accounts. If you specify multiple accounts, separate by a space NOT a comma.')
parser.add_argument('-c', '--custompost', nargs=2, help='Post a custom message. Specify an account then type the message. Make sure you use "" around the message. Use "all" for all accounts.')
parser.add_argument('-l', '--list', action='store_const', const='all', help='List all accounts.')
parser.add_argument('--version', action='version', version='%(prog)s 0.3.3')
args = parser.parse_args()
useArguments()
【问题讨论】:
【参考方案1】:您可以为参数 by 提供自定义 action,我引用:
传递一个实现 动作 API。最简单的方法 是扩展argparse.Action, 提供适当的
__call__
方法。__call__
方法应该 接受四个参数:parser:包含此操作的 ArgumentParser 对象。 命名空间:将由
parse_args()
返回的命名空间对象。大多数操作都会向该对象添加一个属性。 values:关联的命令行参数,应用了任何类型转换。(类型转换使用add_argument()
的 type 关键字参数指定。 option_string:用于调用此操作的选项字符串。 option_string 参数是可选的,如果操作与位置参数相关联,则该参数将不存在。
【讨论】:
在什么情况下这是最好的方法?我看不到所有这些额外代码的用途。但是话又说回来,我几乎没有使用类,所以我可能遗漏了一些东西。 @vlad,它可能用于在提供参数时自动调用函数,这就是你正在做的所有样板文件 - 你只需要将函数设为__call__
的适当子类的 argparse.Action
方法。但是,如果您没有“获得”面向对象的编程,那没关系,您可以按照自己的方式进行(尽管该循环和 if x ==
检查在任何情况下都是多余的 - 只需依次检查哪些参数可能是适当调用的跟随者,在您使用的其他样板中没有附加值)。
接受了这个答案,因为它回答了我的问题。我可能最终会尝试这个来了解它是如何工作的;但这需要对我的代码当前的工作方式进行许多更改(尤其是那里列出的功能)。谢谢!
我喜欢这种方法的简洁性,但限制是自定义操作在解析剩余参数之前开始(即,它们不在 Namespace()
中),所以不清楚可选标志可能会影响自定义操作的行为。【参考方案2】:
见http://docs.python.org/library/argparse.html#sub-commands:
处理子命令的一种特别有效的方法是将
add_subparsers()
方法的使用与对set_defaults()
的调用结合起来,这样每个子解析器都知道它应该执行哪个Python 函数。
简而言之:
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
weather_parser = subparsers.add_parser('get-weather')
weather_parser.add_argument('--bar')
weather_parser.set_defaults(function=get_weather) # !
args = parser.parse_args(['get-weather', '--bar', 'quux'])
print args.function(args)
在这里,我们为命令get-weather
创建一个子解析器,并将函数get_weather
分配给它。
请注意,文档说关键字/属性被命名为 func
,但在 argparse 1.1 中它绝对是 function
。
生成的代码有点冗长,所以我发布了一个小包"argh",它使事情变得更简单,例如:
parser = argparse.ArgumentParser()
add_commands(parser, [get_weather])
print dispatch(parser, ['get-weather', '--bar', 'quux'])
“Argh”可以做更多,但我会让堆栈溢出来回答这个问题。 :-)
【讨论】:
关于 caffinatedmonkey 最近的编辑(“我不会让这个答案堆栈溢出”→“我会让堆栈溢出回答那个”)。新版本听起来不错,但实际上我的意思是,如果我在 textarea 中塞入太多信息,答案会变得臃肿。 =) 您确定func
/function
部分仍然正确吗? func
对我来说很好......
子解析方法不仅丑陋,而且似乎只是将参数解析的任务传递给了较低的级别——你要么必须有单独的函数来传递给每个 func
然后调用您的“真实”功能,或者您的真实功能必须采用通用 args 字典。我想前者对于大型程序来说很好,但是在一个小程序中引入这么多额外的层似乎很荒谬。尽管如此,子解析器似乎是迄今为止最好的选择。
@RyneEverett 实际上,argh 库通过自省签名支持将普通 Python 函数作为命令,因此不需要愚蠢的包装函数。
我的批评没有针对 argh,但我肯定会检查一下。 Argh 似乎本质上是 argparse 子解析器的包装器,所以我对 argparse 的样板要求的烦恼仍然存在。【参考方案3】:
除了--version
,这是一个非常常见的选项,您提供的操作最好被视为“子命令”。
我不知道 argparse 的细节,因为我还没有尝试过 Python 2.7,但您可以看看 svn
命令作为示例,这里是命令行的一些伪代码:
myprog [--version] <command> [<command opts>...]
<command>
在哪里:
add|edit|getweather|post|custompost|list
而<command opts>
是特定于该命令的选项。使用 optparse(类似),这意味着您的命令将在 args
中返回,当调用 parse_args
时,您可以执行以下操作:
opts, args = parser.parse_args()
if opts.version:
...
else:
getattr("do_" + args[0])(*args[1:])
我发现这种模式对调试特别有用,我可以从命令行提供对内部函数的访问,并传递各种参数进行测试。根据您自己的项目调整命令处理程序的选择。
【讨论】:
argparse
实际上正是为这种模式提供了支持。 (optparse
没有。)我自己仍然坚持使用 Python 2.6,所以我也不知道具体细节,但在 documentation 中有解释。
这样做有什么好处,而不是仅仅从我的论点中删除--
。我猜对最终用户来说它最终看起来是一样的,对吧?我想这样对用户也更有意义,因为他们并没有真正使用自定义参数运行程序,而是告诉它做某事。
@vlad003:没错。程序的用户必须选择其中一个且只有一个“选项”,因此它们实际上是对您的程序的命令。然而,这些命令确实可能需要参数。另一种方法是为每个命令编写单独的可执行脚本,在每个命令中分别执行 arg 解析,并调用一个公共代码库来实现。例如,您的脚本可能被称为:myprog-list、myprog-add 等。但是我可能会添加,因为您使用的是 Python-2.7 中的 argparse,可能已经包含了非常精美的参数处理。以上是关于使用 argparse 输出调用函数的主要内容,如果未能解决你的问题,请参考以下文章