argparse 操作或逗号分隔列表的类型

问题描述 投票:0回答:5

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

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

在解析器内部将其转换为

['abcd', 'e', 'fg']
(元组也可以)。

我已经使用

action
type
成功完成了此操作,但我觉得其中一个可能是滥用系统或遗漏了极端情况,而另一个是正确的。不过,我不知道哪个是哪个。

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)
python python-3.x argparse
5个回答
9
投票

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

#!/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']}

3
投票

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

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 因为默认值必须是字符串。


0
投票

您的自定义操作是与其他参数类型在内部完成的最接近的方式。恕我直言,应该在 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()

0
投票

我知道这篇文章很旧,但我最近发现自己正在解决这个问题。我使用

functools.partial
作为轻量级解决方案:

import argparse

from functools import partial

csv_ = partial(str.split, sep=',')

p = argparse.ArgumentParser()
p.add_argument('--stuff', type=csv_)
p.parse_args(['--stuff', 'a,b,c'])
# Namespace(stuff=['a', 'b', 'c'])

如果您不熟悉

functools.partial
,它允许您创建部分“冻结”的函数/方法。在上面的示例中,我创建了一个新函数 (
csv_
),它本质上是
str.split()
的副本,只不过
sep
参数已“冻结”为逗号字符。


0
投票

使用

type
和 lambda 添加我的解决方案进行转换:

parser.add_argument(
  "--myarg",
  default="abc,d,ef",  # conversion only happens if default is a string
  type=lambda t: [s.strip() for s in t.split(',')],
)

这更简洁,所以我认为这可能会有所帮助。有关

type
的更多详细信息,请查看 Python 文档 here(链接到 py3.7,但对于更高版本的 Python 版本是相同的)。

© www.soinside.com 2019 - 2024. All rights reserved.