【问题标题】:argparse argument orderargparse 参数顺序
【发布时间】:2012-02-20 01:44:11
【问题描述】:

我有一个小问题。

我使用argparse 来解析我的论点,而且效果很好。

要获得参数,我会这样做:

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

但是p_args 的问题是我不知道如何让这些参数按它们在命令行中的位置排序,因为它是一个字典。

那么有没有可能按照命令行中的顺序将参数放在元组/列表/有序字典中?

【问题讨论】:

  • 查看显示您如何设置解析器的代码会有所帮助。

标签: python arguments argparse keyword-argument


【解决方案1】:

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

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)

【讨论】:

    【解决方案2】:

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

    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 documentation 了解更多信息。

    【讨论】:

    • 我认为这种方法的一个后果是 argparse 不能/不会对 1,2,3,4,5 进行任何验证(即强制用户只为 optional2 输入合法值)。您必须在代码中遍历 optional2 的值并验证它们。
    【解决方案3】:

    这有点脆弱,因为它依赖于了解 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() 不包含列表中任何参数的病理情况)

    【讨论】:

    • 好吧,只有在搞砸了我的重构并通过一堆测试之后,我才意识到这在 action='store_const' 或 action=' 的情况下根本不起作用store_true' 等。带有 add_argument('-f', action='store_true') 的东西不会将 -f 添加到参数顺序列表中,因为 argparse 在遇到实际参数之前不会向命名空间添加任何值,除非' const' 或 'default' 参数被传递给 add_argument。呸,这可能是一个如此优雅的解决方案..
    【解决方案4】:

    有专门的模块来处理这个:

    https://github.com/claylabs/ordered-keyword-args

    不使用orderedkwargs模块

    def multiple_kwarguments(first , **lotsofothers):
        print first
    
        for i,other in lotsofothers:
             print other
        return True
    
    multiple_kwarguments("first", second="second", third="third" ,fourth="fourth" ,fifth="fifth")
    

    输出:

    first
    second
    fifth
    fourth
    third
    

    关于使用 orderedkwargs 模块

    from orderedkwargs import ordered kwargs  
    @orderedkwargs  
    def mutliple_kwarguments(first , *lotsofothers):
        print first
    
        for i, other in lotsofothers:
            print other
        return True
    
    
    mutliple_kwarguments("first", second="second", third="third" ,fourth="fourth" ,fifth="fifth")
    

    输出:

    first
    second
    third
    fourth
    fifth
    

    注意:在函数上方使用带有装饰器的此模块时,需要单个星号。

    【讨论】:

    • 我分叉了它来处理函数头中的 OrderedDict 和函数体中的 相同语法 与 **kwargs 案例一样的 kwargs。见github.com/zellerede/Ordered-Keyword-Args/tree/OrderedDict
    • @Berci Nice,如果您愿意,您也可以提出拉取请求。
    • 这没有回答问题,这是关于使用 argparse 解析命令行参数的问题,而不是关于函数参数的问题。
    【解决方案5】:

    我需要这个,因为出于日志记录的目的,我喜欢在参数被解析后打印出来。问题是参数没有按顺序打印,这真的很烦人。

    自定义操作类对我不起作用。我还有其他使用不同操作的参数,例如'store_true'default 参数也不起作用,因为如果命令行中没有给出参数,则不会调用自定义操作类。对我有用的是创建一个像这样的包装类:

    import collections
    
    from argparse import ArgumentParser
    
    class SortedArgumentParser():
        def __init__(self, *args, **kwargs):
            self.ap = ArgumentParser(*args, **kwargs)
            self.args_dict = collections.OrderedDict()      
    
        def add_argument(self, *args, **kwargs):
            self.ap.add_argument(*args, **kwargs)
            # Also store dest kwarg
            self.args_dict[kwargs['dest']] = None
    
        def parse_args(self):
            # Returns a sorted dictionary
            unsorted_dict = self.ap.parse_args().__dict__
            for unsorted_entry in unsorted_dict:
                self.args_dict[unsorted_entry] = unsorted_dict[unsorted_entry]
    
            return self.args_dict
    

    优点是add_argument 方法应该具有与原始ArgumentParser 完全相同的功能。缺点是,如果您想要其他方法,则必须为所有方法编写包装。幸运的是,我曾经使用过的只是 add_argumentparse_args,所以这很好地满足了我的目的。如果你想使用父 ArgumentParsers,你还需要做更多的工作。

    【讨论】:

      【解决方案6】:

      这是我基于现有解决方案的简单解决方案:

      class OrderedNamespace(argparse.Namespace):
          def __init__(self, **kwargs):
              self.__dict__["_order"] = [None]
              super().__init__(**kwargs)
          def __setattr__(self, attr, value):
              super().__setattr__(attr, value)
              if attr in self._order:
                  self.__dict__["_order"].clear()
              self.__dict__["_order"].append(attr)
          def ordered(self):
              if self._order and self._order[0] is None:
                  self._order.clear()
              return ((attr, getattr(self, attr)) for attr in self._order)
      
      parser = argparse.ArgumentParser()
      parser.add_argument('--test1', default=1)
      parser.add_argument('--test2')
      parser.add_argument('-s', '--slong', action='store_false')
      parser.add_argument('--test3', default=3)
      
      args = parser.parse_args(['--test2', '2', '--test1', '1', '-s'], namespace=OrderedNamespace())
      
      print(args)
      print(args.test1)
      for a, v in args.ordered():
          print(a, v)
      

      输出:

      OrderedNamespace(_order=['test2', 'test1', 'slong'], slong=False, test1='1', test2='2', test3=3)
      1
      test2 2
      test1 1
      slong False
      

      它允许 add_argument() 中的操作,这对于自定义操作类解决方案来说更难。

      【讨论】:

        猜你喜欢
        • 2013-07-02
        • 2017-02-24
        • 2015-07-25
        • 1970-01-01
        • 2020-10-12
        • 2013-03-30
        • 2011-08-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多