Python + argparse - 如何从命令行获取可选参数的顺序

6
我想知道如何从命令行传递的可选参数中获取顺序,以便使用argparse处理。

我有一个图像处理类,它能够对图像应用不同的操作,比如旋转、裁剪、调整大小等等。

而这些操作应用的顺序通常是至关重要的(例如:您希望在调整图像大小之前先裁剪图像)。

以下是我的代码:

parser = argparse.ArgumentParser(description='Image processing arguments')

parser.add_argument('source_file', help='source file')
parser.add_argument('target_file', help='target file')

parser.add_argument('-resize', nargs=2, help='resize image', metavar=('WIDTH', 'HEIGHT'))
parser.add_argument('-rotate', nargs=1, help='rotate image', metavar='ANGLE')
parser.add_argument('-crop', nargs=4, help='crop image', metavar=('START_X','START_Y','WIDTH','HEIGHT'))

ar = parser.parse_args()

print ar

但是,无论我用什么顺序传递参数给脚本:

cmd.py test.jpg test2.jpg -crop 10 10 200 200 -resize 450 300

cmd.py test.jpg test2.jpg -resize 450 300 -crop 10 10 200 200

在名称空间中,项目始终按相同顺序排列(我想是按字母顺序):

Namespace(crop=['10', '10', '200', '200'], resize=['450', '300'], rotate=None, source_file='test.jpg', target_file='test
2.jpg')

有没有办法按照命令行字符串中的位置对它们进行排序或获取它们的索引?
4个回答

6
您可以随时查看 sys.argv,它是一个列表(因此有序),只需迭代检查哪个参数先出现或使用 list.index() 查看关键字在列表中的位置即可......

sys.argv 包含在命令行中输入的单词列表(这些“单词”的分隔符是空格,除非字符串被引号包围)。这意味着如果用户输入类似于 ./my_proggie -resize 500 这样的内容,则 sys.argv 将包含如下列表:['./my_proggie', '-resize', '500']

5

命名空间是一个简单的对象,其str()根据其__dict__中键的顺序列出其属性。可以使用setattr(namespace, dest, value)设置属性。

一种解决方案是定义一个自定义的Namespace类。例如:

class OrderNamespace(argparse.Namespace):
    def __init__(self, **kwargs):
        self.__dict__['order'] = []
        super(OrderNamespace, self).__init__(**kwargs)
    def __setattr__(self,attr,value):
        self.__dict__['order'].append(attr)
        super(OrderNamespace, self).__setattr__(attr, value)

并使用

args = parser.parse_args(None, OrderNamespace())

生产你提供的两个示例。
OrderNamespace(crop=..., order=[..., 'crop', 'resize'], resize=...)
OrderNamespace(crop=..., order=[..., 'resize', 'crop'], resize=...)
order属性指定了其他属性设置的顺序。初始项用于默认值和文件位置标识。将default=argparse.SUPPRESS添加到参数中可以抑制其中的一些项。这个自定义类可以更加复杂,例如使用一个有序字典,仅记录选定参数的顺序,或使用order控制属性的显示。
另一个选择是使用创建此order属性的自定义Action类,例如:
class OrderAction(argparse._StoreAction):
    def __call__(self, parser, namespace, values, option_string=None):
        setattr(namespace, self.dest, values)
        order = getattr(namespace, 'order') if hasattr(namespace, 'order') else []
        order.append(self.dest)
        setattr(namespace, 'order', order)

OrderNamespace是一个很好的解决方案,如果想要使用其他操作(例如内置操作),那就更好了。非常棒的想法! - Martin Jiřička
不幸的是,当设置默认值时,__setattr__ 方法会为所有默认值调用,无论它们是否被使用。 - Martin Jiřička
如果您不使用SUPPRESS,则默认值将出现在“order”列表的开头,可能还会再次出现在结尾。解析器使用相同的__setattr__来记录默认值和给定值。OrderAction没有这个问题。 - hpaulj

2
我采用了hpaulj的方法:
    class OrderNamespace(argparse.Namespace):
        def __init__(self, **kwargs):
            self.__dict__['order'] = []
            super(OrderNamespace, self).__init__(**kwargs)
        def __setattr__(self,attr,value):
            if value:
              self.__dict__['order'].append(attr)
            super(OrderNamespace, self).__setattr__(attr, value)


    parser.add_argument('-g',action='append',default=argparse.SUPPRESS,help='some action')

通过添加 "if value:" ... 语句,您只会获得每个使用的参数正确次数。

2

有一个问题需要注意,@Martin 的解决方案无法处理这种情况:

parser.add_argument('-s', '--slong', action='store_false')

这是我的解决方案:

import argparse

class OrderedNamespace(argparse.Namespace):
    def __init__(self, **kwargs):
        self.__dict__["_order"] = []
        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):
        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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接