从命令行获取参数,然后从文件获取,再从默认值获取

1 投票
2 回答
1139 浏览
提问于 2025-04-17 15:44

我有一个Python程序,它的运行依赖于一些参数。比如说,其中一个参数是 C,默认值是 3。所以当我不传任何参数运行它时,它会这样做:

$ python parsing.py
C=3

当我加载一个文件作为初始数据时,它可以从那个文件中获取一些参数值。比如,如果我的文件 MakeC5 里写着程序应该用 C=5 来运行,那么我会得到:

$ python parsing.py --file=MakeC5
C=5

另一方面,如果我在命令行中指定了一个不同的值给C作为可选参数,那么这个值会被使用,并且会优先于文件中的值。

$ python parsing.py --C=4
C=4

$ python parsing.py --file=MakeC5 --C=4
C=4

到这里为止,我可以检查一下命令行中指定的值是否和默认值不同,如果相同的话,就用文件中的值,像这样:

if parameters.C == parser.get_default('C'):
    parameters.C = load_file(parameters.file)["C"]

但是这种方法在我在命令行中给出C的默认值时就不管用了,比如:

$ python parsing.py --file=MakeC5 --C=3
C=3

我该怎么处理这种情况呢?有没有一种方法可以避免像这样解析命令行两次:

parameters = parser.parse_args()
parameters_from_file = load_file(parameters.file)
parser.set_defaults(**parameters_from_file)
parameters = parser.parse_args()

这看起来对我来说并不是“显而易见的Python方式”。这就像是在读取一个可以作为参数指定的配置文件,所以我希望能有一个好的解决办法。

2 个回答

1

argsparse这个库里,有一个叫fromfile-prefix-chars的功能,它似乎可以满足你的一些需求,不过它有一些限制。

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--foo', default="ABC")
args = parser.parse_args()
print '#1', args.foo

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--foo', default="ABC")
args = parser.parse_args(['--foo', 'BAR' ])
print '#2', args.foo

import argparse
parser = argparse.ArgumentParser(fromfile_prefix_chars='@')
parser.add_argument('--foo', default="ABC")
args = parser.parse_args(['--foo', 'BAR', '@myargs' ])
print '#3', args.foo

import argparse
parser = argparse.ArgumentParser(fromfile_prefix_chars='@')
parser.add_argument('--foo', default="ABC")
args = parser.parse_args(['@myargs', '--foo', 'BAR' ])
print '#4', args.foo

myargs

--foo
FISH

输出

#1 ABC
#2 BAR
#3 FISH
#4 BAR

注意在命令行中,@myargs的位置是如何影响foo的值的。当它在最前面时,如果有--foo这个选项,它的值会被覆盖,反之亦然。

这看起来可以实现你想要的效果,其中:

默认值 < 配置文件 < 命令行参数

不过,这个方法需要你记得把配置文件放在正确的位置,当然,如果你能把参数重新排序,让它们在最前面,那就没问题了。

希望这些能给你一些启发。

5

假设你使用的是 argparse 模块,那么在添加参数的时候,不要设置默认值。这样如果这个参数没有被提供,它的值就会是 None。你可以检查这个值,而不是解析参数两次:

parser.add_argument('--C'   , action='store', dest='C')
parser.add_argument('--file', action='store', dest='file')
# ...

args = parser.parse_args()
# ...

if args.C is None:
    # Argument `--C` not given on command line
    if args.file is not None:
        # Load the file, set `args.C` if the file contains the `C` option
        ...

if args.C is None:
    # `C` was neither specified on the command line argument given,
    # nor in the configuration file, set default value
    args.C = 3

print 'C =', args.C

撰写回答