Argparse:检查是否有传递任何参数

77

如果未给出参数,我的脚本应该启动演示模式。我尝试了这个:

args = parser.parse_args()
if len(args) == 0:
    run_demo()
else:
    # evaluate args

使用 argparse 模块时,args 对象是一个 Namespace 类型的对象,而不是列表类型,因此无法使用 len() 函数。如果要计算参数数量,可以使用 len(vars(args))

那么如何实现我想要的功能呢?


使用try except捕捉TypeError,以便您知道未传递任何内容。 - avasal
9
@avasal,len(args) 始终会引发 TypeError 错误。 - huon
1
陷阱警告 - 如果您计划使用pdb进行调试,请不要将“args”用作变量名...它是pdb关键字,而且在看起来已正确实例化的同时会给您带来空白结果。如果您确实使用了它,“!args”在pdb中将向您显示实际对象。 - Aaron Williams
8个回答

125
如果您的目标是检测命令没有给出参数,那么通过argparse实现这一点是错误的方法(正如Ben所指出的那样)。
简单思考! :-) 我相信argparse不会清除sys.argv。因此,如果if not len(sys.argv) > 1,则用户未提供任何参数。

1
它能够正常工作,而且这可能是做这件事情的更好/简单的方式:D - Cédric Julien
接受了这个答案,因为它解决了我的问题,而我不必重新考虑事情。=> 我太懒了 ;) - Framester
比起我建议检查“所有”选项是否为None,这个问题的实际建议要多得多。 - Ben
请注意,这种方法无法检查传递给任意嵌套子解析器的参数数量。 - Jonathan Komar

23

argparse让你能够通过定义规范和解析命令行来在Namespace对象中设置所有添加到解析器的参数所提到的变量。如果你设置了默认值,那么这些变量将具有该默认值,如果它们没有出现在命令行中,它们不会缺失在Namespace对象中。如果你没有指定默认值,则有一个隐含的默认值为None。因此,检查Namespace对象的长度,无论如何管理它,都没有意义,作为一种检查是否解析了任何参数的方法;它应该始终具有相同的长度。

相反,如果你知道你有一堆没有默认值的参数,并且你想检查它们中是否有任何一个被设置为任何非None值......就这样做。你可以使用列表推导式和vars函数循环遍历它们,而不必重复add_argument调用中名称的列表,就像Martijn的答案中展示的那样。

如果你的一些参数有默认值,那么情况就有点棘手了,尤其是当它们具有可以在命令行上明确提供的默认值时(例如,默认为0的数字参数使得无法区分用户提供的0和默认值)。在这种情况下,我不确定是否有一个通常情况下不需要知道参数是什么的解决方案。


14

我知道这是一个旧的线程,但我找到了一个更直接的解决方案,可能也对其他人有用:

您可以检查是否传递了任何参数:

if any(vars(args).values()):
    # evaluate args

或者,如果没有传递参数(注意使用not运算符):

if not any(vars(args).values()):
    run_demo()

解释:

  • parse_args() 返回一个包含每个参数名称及其相关值的 "Namespace" 对象。 例如: Namespace(arg1='myfile.txt', arg2='some/path/to/some/folder')

  • 如果没有传入参数parse_args() 将返回相同的对象,但所有值都为 None。 例如: Namespace(arg1=None, arg2=None)

然而,这个对象不可迭代,所以您必须使用vars()将其转换为一个dict,以便我们可以访问这些值。

最后,因为我们现在有了一个dict,我们可以使用 .values() 获取所有值(作为一个list),并使用内置的any()函数来检查是否有任何一个值不为None。 为了更清楚: 如果您向 any() 提供的列表中没有单个值为None, False0(请参见文档),则它将返回False

希望对你有所帮助。


2
如果您有默认参数,它们将覆盖None,因此这不起作用。 - hkh
此外,store_truestore_false 类型的参数会隐式设置默认值。store_true 的默认值是 False,这不会破坏你的测试(尽管它会破坏 any(var is not None for var in vars(args).values())),但 store_false 的默认值是 True,这肯定会破坏测试。 - undefined

13

不要使用argparse,而只需使用sys.argvargparse会创建一个Namespace对象,它将根据您在调用脚本时使用的参数给出一个"dict"(字典)及其值。

以下是我过去所做的:

args = parser.parse_args()
if len(sys.argv) == 1:
    parser.print_help()
    sys.exit()
return args

11
如果确实需要参数编号(出于任何原因),我发现这段代码非常有用(但不知道它有多少优化,我会感激任何对它的意见)。
args = parser.parse_args()
print( len( vars(args) ) )

这个版本仅计算 -xx 参数,而不包括传递的任何其他值。

如果想要所有东西(包括传递的值),那么只需像之前提到的那样使用 len(sys.argv)


3
假设我们以下面的例子来扩展您的内容,以实现完整性:
#!/usr/bin/env python3

import argparse

...
def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('input', nargs='?' action='store')
    parser.add_argument('-l', '--length', type=int, action='store')
    parser.add_argument('-v', '--verbose', action='store_true')
    args = parser.parse_args()
    if (args.input == None and args.length == None):
        parser.print_help()
    else:
        print(args)

if __name__ == '__main__':
    main()

在这个例子中,@Ben提到的Namespace对象是args。从parser.add_argument中的字符串创建了一个变量。您可以通过args.inputargs.lengthargs.verbose访问它。您可以执行print(args)来验证这一点,它实际上将显示类似于此的内容:

Namespace(input=None, length=None, verbose=False)

由于 verbose 设置为 True,如果存在且输入和长度只是变量,则不必实例化(没有提供参数)。

如果您想确保不同时提供两个属性,group = parser.add_mutually_exclusive_group() 也可以提供帮助。

有关详细信息,请参阅:


创意。喜欢这个答案! - 0xInfection

2
我将2dvisio的概念扩展到统计非零或无效参数的数量:
vm_opts = parser.parse_args()
v = vars(vm_opts)
n_args = sum([ 1 for a in v.values( ) if a])

如果使用unittest来测试带有argparse的内容,那么这将非常有用,因为接受的答案可能会出现问题。 - cardamom

0

对于最简单的情况,您想要检查是否已传递了所有输入中相同的单个参数类型,您可以使用argparsenumpy三个步骤来完成。

import argparse
import numpy as np

args = parser.parse_args()
# namespace to dictionary
args_dict = vars(args)
# unpack values from dictionary, pass to array
values = np.array([*args_dict.values()])
# Check if the defaults have changed
args_indices = np.where(values != default)[0]
# Did we pass any arguments?
if len(values) == len(args_indices):
   print("No arguments were passed")

长度被用作代理来检查是否传递了任何没有参数。如果您想知道哪个已传递,您可以解包键并检查更改的索引。

np.array()接受逻辑运算符以处理更复杂的情况。


你可以通过使用defaults = vars(parser.parse_args([]))来获取一个包含所有默认值的Namespace - Girardi
你说得对,谢谢。你可以从代码片段中选择默认值 :) - mobiuscreek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接