【问题标题】:argparse action or type for comma-separated list逗号分隔列表的 argparse 操作或类型
【发布时间】:2019-02-07 11:13:15
【问题描述】:

我想创建一个命令行标志,可以用作

./prog.py --myarg=abcd,e,fg

并在解析器内部将其转换为['abcd', 'e', 'fg'](元组也可以)。

我已经使用actiontype 成功地做到了这一点,但我觉得一个可能是系统滥用或缺少极端情况,而另一个是正确的。但是,我不知道哪个是哪个。

action:

import argparse

class SplitArgs(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        setattr(namespace, self.dest, values.split(','))


parser = argparse.ArgumentParser()
parser.add_argument('--myarg', action=SplitArgs)
args = parser.parse_args()
print(args.myarg)

改为type:

import argparse

def list_str(values):
    return values.split(',')

parser = argparse.ArgumentParser()
parser.add_argument('--myarg', type=list_str)
args = parser.parse_args()
print(args.myarg)

【问题讨论】:

  • 还有一个选项 - 在解析后拆分 args.myarg 字符串。他们都在做同样的事情,只是在不同的时间。或者你可以指定nargs='+'(或3),然后输入'--myarg abcd e fg'。
  • 在期待一个逗号分隔的列表时要小心——如果用户包含空格,它会变得混乱。 '--myarg=abcd,e, fg'。除非他们引用字符串:'--myarg "abcd, e, fg"'。这就是为什么没有内置任何东西的部分原因。
  • @hpaulj 这很容易通过排除空值的理解来处理。 setattr(namespace, self.dest, [v for v in values.split(',') if v])

标签: python python-3.x argparse


【解决方案1】:

最简单的解决方案是将参数视为字符串并拆分。

#!/usr/bin/env python3

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("--myarg", type=str)
d = vars(parser.parse_args())
if "myarg" in d.keys():
    d["myarg"] = [s.strip() for s in d["myarg"].split(",")]
print(d)

结果:

$ ./toto.py --myarg=abcd,e,fg
{'myarg': ['abcd', 'e', 'fg']}
$ ./toto.py --myarg="abcd, e, fg"
{'myarg': ['abcd', 'e', 'fg']}

【讨论】:

  • 这是一个很好的解决方案,因为 Ryan 建议的方法都试图将一些功能放入 argparse 的 add_argument 方法中,而实际上这里所需的任务是处理收到的参数。正如 hpaulj 所指出的,对于参数列表的外观以及它是否是字符串列表、整数列表、浮点数或其他什么,没有明确的默认定义。根据 argparse 的文档,action 的含义有所不同,“list”在这种情况下不是有效的type
【解决方案2】:

我发现您的第一个解决方案是正确的。原因是它可以让您更好地处理默认值:

names: List[str] = ['Jane', 'Dave', 'John']

parser = argparse.ArumentParser()
parser.add_argument('--names', default=names, action=SplitArgs)

args = parser.parse_args()
names = args.names

这不适用于 list_str,因为默认值必须是字符串。

【讨论】:

    【解决方案3】:

    您的自定义操作是与其他参数类型在内部完成的最接近的方式。恕我直言,应该在 stdlib 中的 argparse 中添加一个 _StoreCommaSeperatedAction,因为它是一种常见且有用的参数类型,

    它也可以与添加的默认值一起使用。

    这是一个不使用动作的示例(没有 SplitArgs 类):

    class Test:
        def __init__(self):
            self._names: List[str] = ["Jane", "Dave", "John"]
    
        @property
        def names(self):
            return self._names
    
        @names.setter
        def names(self, value):
            self._names = [name.strip() for name in value.split(",")]
    
    
    test_object = Test()
    parser = ArgumentParser()
    parser.add_argument(
        "-n",
        "--names",
        dest="names",
        default=",".join(test_object.names),  # Joining the default here is important.
        help="a comma separated list of names as an argument",
    )
    print(test_object.names)
    parser.parse_args(namespace=test_object)
    print(test_object.names)
    

    这是另一个在类中完全使用 SplitArgs 类的示例

        """MyClass
    Demonstrates how to split and use a comma separated argument in a class with defaults
    """
    import sys
    from typing import List
    from argparse import ArgumentParser, Action
    
    
    class SplitArgs(Action):
        def __call__(self, parser, namespace, values, option_string=None):
            # Be sure to strip, maybe they have spaces where they don't belong and wrapped the arg value in quotes
            setattr(namespace, self.dest, [value.strip() for value in values.split(",")])
    
    
    class MyClass:
        def __init__(self):
            self.names: List[str] = ["Jane", "Dave", "John"]
            self.parser = ArgumentParser(description=__doc__)
            self.parser.add_argument(
                "-n",
                "--names",
                dest="names",
                default=",".join(self.names),  # Joining the default here is important.
                action=SplitArgs,
                help="a comma separated list of names as an argument",
            )
            self.parser.parse_args(namespace=self)
    
    
    if __name__ == "__main__":
        print(sys.argv)
        my_class = MyClass()
        print(my_class.names)
        sys.argv = [sys.argv[0], "--names", "miigotu, sickchill,github"]
        my_class = MyClass()
        print(my_class.names)
    

    下面是如何在基于函数的情况下执行此操作,包括默认值

    class SplitArgs(Action):
        def __call__(self, parser, namespace, values, option_string=None):
            # Be sure to strip, maybe they have spaces where they don't belong and wrapped the arg value in quotes
            setattr(namespace, self.dest, [value.strip() for value in values.split(",")])
    
    names: List[str] = ["Jane", "Dave", "John"]
    parser = ArgumentParser(description=__doc__)
    parser.add_argument(
        "-n",
        "--names",
        dest="names",
        default=",".join(names),  # Joining the default here is important.
        action=SplitArgs,
        help="a comma separated list of names as an argument",
    )
    parser.parse_args()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-09-16
      • 2013-07-03
      • 1970-01-01
      • 2013-05-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-26
      相关资源
      最近更新 更多