Argparse默认,因为总是调用函数

时间:2018-06-21 11:35:57

标签: python argparse

// edit 1:略微更改了get_variable-忘记添加传递给它的另一个参数(正在从内存写入它,对此感到抱歉)。

我对default中的argparser值有疑问。

某些值(如果不存在于命令行中)则使用os.env从环境中获取,如果没有,则从DEFAULT_FOR_VARIABLE 获取:

def get_variable(name, DEFAULT_FOR_VARIABLE = ''):
    if name in os.environ:
        return os.environ[name]
    print("no default value")
    return DEFAULT_FOR_VARIABLE

这是main()中的解析方式:

parser = argparse.ArgumentParser(description=MODULE_NAME)
parser.add_argument('--test_arg', default=get_variable(VAR_NAME, DEFAULT_FOR_TEST_ARG))
args = parser.parse_args()
print(args.test_arg)

无论是否传递参数,都将调用get_variable函数,并且如果os.environ中没有值,则会执行print(让我知道缺少参数) ),即使传递了一个值:

λ python Parser_Test.py --test_arg test_arg
no default value
test_arg

未传递参数时,它按预期工作:

λ python Parser_Test.py
No default value

但是设置了DEFAULT_FOR_TEST_ARG时:

λ python Parser_Test.py
  No default value
  DEFAULT_VALUE_FOR_TEST_ARG

检查每个已解析的参数也很困难,因为没有办法像argparse那样对它们进行迭代-我要检查的用户很少。

有没有办法改变这种行为?还是在这种情况下应该使用非标准模块来解析参数?

3 个答案:

答案 0 :(得分:1)

不确定我是否完全理解,但是您不能这样做吗?

def get_variable(name):
    if name in os.environ:
        return os.environ[name]
    else:
        print("no default value")
        return 'empty'

或者:

parser = argparse.ArgumentParser(description=MODULE_NAME)
parser.add_argument('--test_arg',dest='test',nargs='?', default="empty")
args = parser.parse_args()
if args.test == "empty":
    if name in os.environ:
        newGlobalVar = os.environ["name"]
        print("no default value")
    else:
        newGlobalVar = args.test 

答案 1 :(得分:0)

当使用get_variable(VAR_NAME)方法时,解释器将评估

add_argument。在python函数中,参数在传递给函数之前先经过评估。

argparse会延迟评估默认值(如果它是字符串):

In [271]: p = argparse.ArgumentParser()
In [272]: p.add_argument('-f', type=int, default='12');
In [273]: p.parse_args('-f 23'.split())
Out[273]: Namespace(f=23)
In [274]: p.parse_args([])
Out[274]: Namespace(f=12)

此处,如果未提供-f,则'12'将传递给type函数:

int('12')

或使用自定义type

In [275]: def mytype(astr):
     ...:     print('eval',astr)
     ...:     return int(astr)
In [276]: p.add_argument('-g', type=mytype, default='12');
In [277]: p.parse_args([])
eval 12
Out[277]: Namespace(f=12, g=12)
In [278]: p.parse_args(['-g','3'])
eval 3
Out[278]: Namespace(f=12, g=3)

但是在您的情况下,您想有条件地评估的代码可能无法通过type函数处理。也就是说,您不会以与用户提供的字符串相同的方式评估默认值。

因此,解析后的测试可能最有意义。默认默认值为None,可以很容易地对其进行测试:

if args.test is None:
     args.test = 'the proper default'

用户无法提供任何会产生None的字符串,因此这是安全的default


出于好奇,我写了一个type,在os.environ中查找了一个名字:

In [282]: def get_environ(name):
     ...:     if name in os.environ:
     ...:         return os.environ[name]
     ...:     raise argparse.ArgumentTypeError('%s not in environ'%name)

In [283]: p.add_argument('-e', type=get_environ, default='DISPLAY');

没有参数,它将查找默认的os.environ['DISPLAY']

In [284]: p.parse_args([])
eval 12
Out[284]: Namespace(e=':0', f=12, g=12)

具有有效名称:

In [289]: p.parse_args(['-e','EDITOR'])
eval 12
Out[289]: Namespace(e='nano', f=12, g=12)

并在名称无效时引发错误:

In [290]: p.parse_args(['-e','FOO'])
usage: ipython3 [-h] [-f F] [-g G] [-e E]
ipython3: error: argument -e: FOO not in environ
An exception has occurred, use %tb to see the full traceback.

我知道这不是您的目标,但是它可以让您了解如果要延迟评估默认值的可能性。

答案 2 :(得分:0)

这是在python 3.6中使用lambda作为默认值的方法。我认为这是OP想要实现的目标。默认值不会立即得到评估。您可以轻松找到它们并在for循环中调用它们以解析值。我在t2参数中加入了默认字符串,以表明正常的默认设置在这种情况下仍然可以正常工作。

import argparse
import os


def get_value(var, dflt):
    if var in os.environ:
        return os.environ[var]
    return dflt


parser = argparse.ArgumentParser(description=os.path.splitext(os.path.basename(__file__))[0])
parser.add_argument('--t1', default=lambda: get_value('t1_value', 't1 default'))
parser.add_argument('--t2', default='t2 default')
args = parser.parse_args()
print("Arguments have been parsed")
print(f"--t1: {args.t1}")
print(f"--t2: {args.t2}")


print("Lazily getting defaults")
for key in vars(args):
    f = args.__dict__[key]
    if callable(f):
        print(f'Getting default value for {key}')
        args.__dict__[key] = f()

print(f"--t1: {args.t1}")
print(f"--t2: {args.t2}")

结果:

Connected to pydev debugger (build 202.7660.27)
Arguments have been parsed
--t1: <function <lambda> at 0x000002425DD3FAE8>
--t2: t2 default
Lazily getting defaults
Getting default value for t1
--t1: t1_default
--t2: t2 default

Process finished with exit code 0

您可以使用专门的类来做类似的事情,但是我认为lambda更为简洁并且本质上是相同的。