Python 如何解析1-3指定的范围并返回列表

Python 如何解析1-3指定的范围并返回列表,python,parsing,command-line,range,argparse,Python,Parsing,Command Line,Range,Argparse,嗨,基本上我想在参数中添加一个--cid参数,当在命令行--cid1-3 7 22中给出时,解析的参数是一个列表[1,2,3,7,22] 当前尝试: from argparse import ArgumentParser, ArgumentTypeError import re def parseNumList(string): print 'in string:',string if '-' in string: m = re.match(r'(\d+)(?:

嗨,基本上我想在参数中添加一个--cid参数,当在命令行--cid1-3 7 22中给出时,解析的参数是一个列表[1,2,3,7,22]

当前尝试:

from argparse import ArgumentParser, ArgumentTypeError
import re

def parseNumList(string):
    print 'in string:',string
    if '-' in string:
        m = re.match(r'(\d+)(?:-(\d+))?$', string)
        # ^ (or use .split('-'). anyway you like.)
        if not m:
            raise ArgumentTypeError("'" + string + "' is not a range of number. Expected forms like '0-5' or '2'.")
        start = m.group(1)
        end = m.group(2) or start
        return list(range(int(start,10), int(end,10)+1))
    else:
        return string

parser = ArgumentParser()
parser.add_argument('--cid', type=parseNumList,nargs='*')

args = parser.parse_args()
print(args)
但结果是,我不想要嵌套列表,我该怎么做?(我知道我可以再次手动解析args.cid并重新分配它,但我可以在argparse本身中完成所有这一切吗?使用自定义操作或自定义类型?)

编辑: 让它与自定义操作一起工作,但感觉这不是最好的方式。有什么建议吗

from argparse import ArgumentParser, ArgumentTypeError,Action
import re

class CustomRangeAction(Action):
    def __call__(self, parser, namespace, values, option_string=None):
        print 'running action for ' + repr(values)

        flatten = []

        for v in values:
            try:
                m = re.match(r'(\d+)(?:-(\d+))?$', v)  
                if m:
                    start = m.group(1)
                    end = m.group(2) or start
                    flatten.extend(list(range(int(start,10), int(end,10)+1)))
                else:
                    flatten.append(int(v))
            except:
                continue

        flatten = sorted(list(set(flatten)))
        flatten.reverse()

        setattr(namespace, self.dest, flatten)


parser = ArgumentParser()
parser.add_argument('--cid', action=CustomRangeAction,nargs='*')

args = parser.parse_args()
print(args)

您可以将以下代码用于
parseNumList
方法。我假设
input\u string
是该方法的输入

>>> input_string = '1-3 7 22'
>>> input_list = input_string.split()
>>> alist = []
>>> for item in input_list:
...     if '-' in item:
...             a, b = item.split('-')
...             alist.extend(range(int(a), int(b) + 1))
...     else:
...             alist.append(int(item))
...
>>> alist
[1, 2, 3, 7, 22]

您可以在任何适当的地方添加错误和边界检查

正确的方法是在
add_argument
中使用
action=
而不是
type=
来定义特殊行为。当每个参数都是特殊类型时,应该使用type(例如,每个参数都是表示数字的十六进制字符串)。在这种情况下,您的一些论点需要被夸大为多个论点

为了定义一个动作,您需要定义一个新类(从
argparse.action
)并实现
\u调用(self、parser、namespace、values、option\u string=None)
方法:

from argparse import ArgumentParser, ArgumentTypeError, Action
import re

class InflateRange(Action):
    def __call__(self, parser, namespace, values, option_string=None):
        print('%r %r %r' % (namespace, values, option_string))
        lst = []
        for string in values:
            print 'in string:',string
            if '-' in string:
                m = re.match(r'(\d+)(?:-(\d+))?$', string)
                # ^ (or use .split('-'). anyway you like.)
                if not m:
                    raise ArgumentTypeError("'" + string + "' is not a range of number. Expected forms like '0-5' or '2'.")
                start = m.group(1)
                end = m.group(2) or start
                lst.extend(list(range(int(start,10), int(end,10)+1)))
            else:
                 lst.append(int(string))
            setattr(namespace, self.dest, lst)

parser = ArgumentParser()
parser.add_argument('--cid', action=InflateRange, nargs='*')

args = parser.parse_args()
print(args)
这样,您将获得:

> python test.py --cid 1-3 7 22
Namespace(cid=None) ['1-3', '7', '22'] '--cid'
in string: 1-3
in string: 7
in string: 22
Namespace(cid=[1, 2, 3, 7, 22])
有关更多信息,请查看答案正确,但我必须更改
InflateRange
类的代码以使其实际工作

此外,为了保留
argparse
中包含的错误,引发的异常需要是
ArgumentError
类型(请参见答案)

自定义操作的代码如下所示:

class InflateRange(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        lst = []

        limits = values.split('-')
        if len(limits) > 2:
            raise argparse.ArgumentError(self, "%r is not a range of numbers." % part )
        start = limits[0]
        if start == '': start = '1'
        end = limits[-1]
        try:
            lst.extend(list(range(int(start), int(end)+1)))
        except ValueError:
            raise argparse.ArgumentError(self, "%r is not a range of numbers." % part )

        setattr(namespace, self.dest, lst)
except:
切勿使用裸
except
。在您的情况下,您可能只想检查
ValueError
TypeError
,因此请使用:
except(ValueError,TypeError):继续检查。这正是我要找的!附带说明:如果在参数格式错误的情况下引发
ArgumentTypeError(errMsg)
,它将打印堆栈跟踪并中止。如果改用
parser.error(errMsg)
ArgumentParser.error(parser,errMsg)
,它将打印错误消息和用法并退出,隐藏异常详细信息。
class InflateRange(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        lst = []

        limits = values.split('-')
        if len(limits) > 2:
            raise argparse.ArgumentError(self, "%r is not a range of numbers." % part )
        start = limits[0]
        if start == '': start = '1'
        end = limits[-1]
        try:
            lst.extend(list(range(int(start), int(end)+1)))
        except ValueError:
            raise argparse.ArgumentError(self, "%r is not a range of numbers." % part )

        setattr(namespace, self.dest, lst)