argparse 参数顺序

22 投票
6 回答
21358 浏览
提问于 2025-04-17 11:11

我遇到了一点小问题。

我使用 argparse 来解析我的参数,这个工具工作得很好。

为了获取这些参数,我这样做:

p_args = parser.parse_args(argv)
args = dict(p_args._get_kwargs())

但是关于 p_args 的问题是,我不知道怎么按照命令行中的顺序来获取这些参数,因为它是一个字典。

那么,有没有办法把这些参数按命令行的顺序放在一个元组、列表或者有序字典里呢?

6 个回答

6

这个方法有点脆弱,因为它依赖于对 argparse.ArgumentParser 内部工作原理的理解。不过在不重写 argparse.ArgumentParser.parse_known_args 的情况下,我用的是这个方法:

class OrderedNamespace(argparse.Namespace):
    def __init__(self, **kwargs):
        self.__dict__["_arg_order"] = []
        self.__dict__["_arg_order_first_time_through"] = True
        argparse.Namespace.__init__(self, **kwargs)

    def __setattr__(self, name, value):
        #print("Setting %s -> %s" % (name, value))
        self.__dict__[name] = value
        if name in self._arg_order and hasattr(self, "_arg_order_first_time_through"):
            self.__dict__["_arg_order"] = []
            delattr(self, "_arg_order_first_time_through")
        self.__dict__["_arg_order"].append(name)

    def _finalize(self):
        if hasattr(self, "_arg_order_first_time_through"):
            self.__dict__["_arg_order"] = []
            delattr(self, "_arg_order_first_time_through")

    def _latest_of(self, k1, k2):
        try:
            print self._arg_order
            if self._arg_order.index(k1) > self._arg_order.index(k2):
                return k1
        except ValueError:
            if k1 in self._arg_order:
                return k1
        return k2

这个方法的原理是 argparse.ArgumentParser.parse_known_args 会遍历所有选项一次,为每个参数设置默认值。也就是说,用户指定的参数会在 __setattr__ 第一次遇到已经见过的参数时开始生效。

用法:

options, extra_args = parser.parse_known_args(sys.argv, namespace=OrderedNamespace())

你可以查看 options._arg_order 来了解用户指定的命令行参数的顺序,或者使用 options._latest_of("arg1", "arg2") 来查看在命令行中是 --arg1 还是 --arg2 被指定得更晚(对我来说,这个功能很重要:我想知道两个选项中哪个会覆盖另一个)。

更新:我需要添加 _finalize 方法来处理一种特殊情况,即 sys.argv() 列表中没有任何参数的情况。

6

如果你想知道在你的解析器中参数出现的顺序,可以这样设置解析器:

import argparse

parser = argparse.ArgumentParser(description = "A cool application.")
parser.add_argument('--optional1')
parser.add_argument('positionals', nargs='+')
parser.add_argument('--optional2')

args = parser.parse_args()
print args.positionals

这里有个快速的示例来运行这段代码:

$ python s.py --optional1 X --optional2 Y 1 2 3 4 5
['1', '2', '3', '4', '5']

注意,args.positionals 是一个列表,里面按顺序放着位置参数。想了解更多信息,可以查看argparse 的文档

21

为了保持参数的顺序,我使用了一个自定义的操作,像这样:

import argparse
class CustomAction(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        if not 'ordered_args' in namespace:
            setattr(namespace, 'ordered_args', [])
        previous = namespace.ordered_args
        previous.append((self.dest, values))
        setattr(namespace, 'ordered_args', previous)
parser = argparse.ArgumentParser()
parser.add_argument('--test1', action=CustomAction)
parser.add_argument('--test2', action=CustomAction)

使用它的例子是:

>>> parser.parse_args(['--test2', '2', '--test1', '1'])
Namespace(ordered_args=[('test2', '2'), ('test1', '1')], test1=None, test2=None)

撰写回答