【问题标题】:Unpacking arguments from argparse从 argparse 解包参数
【发布时间】:2016-06-19 17:57:21
【问题描述】:

我一直在编写一些命令行 python 程序并使用argparse 来完成它。我一直在按如下方式构建我的代码。

def main(arg1, arg2):
    # magic
    pass

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('arg1')
    parser.add_argument('arg2')

    args = parser.parse_args()

    main(args.arg1, args.arg2)

不得不喊arg1arg2 3 次真的超级烦人。我理解必须做两次。

有没有办法将parse_args 函数返回的命名空间视为一个元组?或者更好的是作为可选参数的元组和字典并进行解包?

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('arg1')
    parser.add_argument('arg2')
    parser.add_argument('--opt-arg', default='default_value')

    args, kwargs = parser.magic_method_call_that_would_make_my_life_amazing()

    # I get goosebumps just thinking about this
    main(*args, **kwargs)

【问题讨论】:

    标签: python argparse


    【解决方案1】:

    https://docs.python.org/3/library/argparse.html#the-namespace-object

    这个类故意简单,只是一个具有可读字符串表示的对象子类。如果您更喜欢类似 dict 的属性视图,可以使用 标准 Python 习语 vars()

    >>>
    >>> parser = argparse.ArgumentParser()
    >>> parser.add_argument('--foo')
    >>> args = parser.parse_args(['--foo', 'BAR'])
    >>> vars(args)
    {'foo': 'BAR'}
    

    请注意,从 optparseargparse 的一大进步或至少是变化之一是位置参数(例如您的参数)被视为与可选项相同。它们都出现在 args Namespace 对象中。在optparse 中,位置只是解析已定义选项的剩余部分。通过省略参数并使用parse_known_args,您可以在argparse 中获得相同的效果:

    parser = argparse.ArgumentParser()
    args, extras = parser.parse_known_args()
    

    args 现在是一个命名空间,extras 是一个列表。然后您可以将您的函数称为:

    myfoo(*extras, **vars(args))
    

    例如:

    In [994]: import argparse
    In [995]: def foo(*args, **kwargs):
       .....:     print(args)
       .....:     print(kwargs)
       .....:     
    In [996]: parser=argparse.ArgumentParser()
    In [997]: parser.add_argument('-f','--foo')
    Out[997]: _StoreAction(option_strings=['-f', '--foo'], dest='foo', nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None)
    In [998]: args,extras = parser.parse_known_args(['-f','foobar','arg1','arg2'])
    In [999]: args
    Out[999]: Namespace(foo='foobar')
    In [1000]: extras
    Out[1000]: ['arg1', 'arg2']
    In [1001]: foo(*extras, **vars(args))
    ('arg1', 'arg2')
    {'foo': 'foobar'}
    

    同样的argparse 段落表明您可以定义自己的Namespace 类。定义一个行为像字典(用作**args)和命名空间的行为并不难。 argparse 的所有要求是它可以与 getattrsetattr 一起使用。

    In [1002]: getattr(args,'foo')
    Out[1002]: 'foobar'
    In [1004]: setattr(args,'bar','ugg')
    In [1005]: args
    Out[1005]: Namespace(bar='ugg', foo='foobar')
    

    另一个标准 Python 功能让我将 vars(args) 作为元组传递:

    In [1013]: foo(*vars(args).items())
    (('foo', 'foobar'), ('bar', 'ugg'))
    {}
    

    对于去年 1 月的类似回答:https://stackoverflow.com/a/34932478/901925

    Neatly pass positional arguments as args and optional arguments as kwargs from argpase to a function

    在那里我给出了如何在解析后将“位置”与“可选”分开的想法。


    这是一个自定义命名空间类,在其 API 中包含一种将自身作为字典返回的方法:

    In [1014]: class MyNameSpace(argparse.Namespace):
       ......:     def asdict(self):
       ......:         return vars(self)
       ......:     
    In [1015]: args = parser.parse_args(['-f','foobar'], namespace=MyNameSpace())
    In [1016]: args
    Out[1016]: MyNameSpace(foo='foobar')
    In [1017]: foo(**args.asdict())
    ()
    {'foo': 'foobar'}
    

    另一个想法 - 使用多个 nargs (2,'*','+') 之一作为位置参数。然后在将其传递给您的函数时,您只需键入一个名称。

    parser.add_argument('pos',nargs='+')
    args = ...
    args.pos # a list, possibly empty
    foo(*args.pos, **vars(args))
    

    【讨论】:

      【解决方案2】:

      您可以看到here 提出的类似问题。

      编辑:寻找一种不使用内部方法的方法,我发现this discussion 建议使用vars()。这很好用:

      import argparse
      
      def main(arg1, arg2):
          print arg1, arg2
      
      if __name__ == '__main__':
          parser = argparse.ArgumentParser()
          parser.add_argument('arg1')
          parser.add_argument('arg2')
      
          args = parser.parse_args()
          main(**vars(args))
      

      【讨论】:

      • 这是一个很好的答案......我宁愿被图书馆设计所困扰,也不愿依赖不属于公共 api 的东西,因为我是个小人物,我最后完成在生活中。
      • 干杯@BenHoff :-) 如果它适合你,你可以accept我的答案。
      • 对不起,我错过了一个但是。这是一个很好的答案.... 但是我宁愿被图书馆设计所困扰,也不愿依赖不属于公共 api 的东西,因为我是一个小人物,我最后完成在生活中。
      • 你也可以**args.__dict__
      【解决方案3】:

      做错了什么

      if __name__ == '__main__':
          # do argparse stuff as above
          main(args)
      

      也就是说,您为什么对提供main() 位置参数如此感兴趣?

      老实说,我通常在模块开头解析 a) [用于小型脚本等] 的参数,它为我提供了一个在所有函数范围内的变量,或者 b) [通常] 在内部main()如果我用你的成语:

      def parse_arguments():
          parser = argparse.ArgumentParser()
          parser.add_argument('arg1')
          parser.add_argument('arg2')
          args = parser.parse_args()
          return args
      
      def main():
          args = parse_arguments()
          # do stuff with args.arg1 and args.arg2 
      

      【讨论】:

      • 如果我从另一个包导入/调用它,我不想将 args 打包到我的 main 函数的元组中。我很喜欢parse_arguments 方法调用。我确实很喜欢。
      猜你喜欢
      • 2018-07-25
      • 2018-12-14
      • 2020-07-14
      • 2021-07-13
      • 2015-11-08
      • 2014-03-04
      • 1970-01-01
      • 1970-01-01
      • 2015-03-04
      相关资源
      最近更新 更多