python + argparse - how to get order of optional arguments from command line
I would like to know how to get order of optional argument passed from commandline to argparse
I have image processing class which is able to apply different actions to image - like rotate, crop, resize...
And order in which these actions are applied is often essential (for example: you want to crop image before you resize it)
I have this code:
parser = argparse.ArgumentParser(description='Image processing arguments')
parser.add_argument('source_file', help='source file')
parser.add_argument('target_fi开发者_如何转开发le', 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
But - no matter in which order I pass parameters to script:
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
in Namespace items are always in same order (alphabetical I suppose):
Namespace(crop=['10', '10', '200', '200'], resize=['450', '300'], rotate=None, source_file='test.jpg', target_file='test
2.jpg')
Is there way to order them by position in command line string or to get their index?
You could always peek at sys.argv
which is a list (and thus ordered) and simply iterate over it checking which argument comes first or use the list.index()
to see the respective positions of your keywords in the list...
sys.argv
contains a list of the words entered in the command line (the delimiter of such "word" is a space unless a string was surrounded by quotation marks). This means that if the user entered something like ./my_proggie -resize 500
then sys.argv
would contain a list like this: ['./my_proggie', '-resize', '500']
.
The Namespace is a simple object whose str()
lists its attributes according to the order of the keys in its __dict__
. Attributes are set with setattr(namespace, dest, value)
.
One solution is to define a custom Namespace class. For example:
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)
and use
args = parser.parse_args(None, OrderNamespace())
producing for your two examples
OrderNamespace(crop=..., order=[..., 'crop', 'resize'], resize=...)
OrderNamespace(crop=..., order=[..., 'resize', 'crop'], resize=...)
The order
attribute gives the order in which the other attributes are set. The initial items are for defaults and the file positionals. Adding default=argparse.SUPPRESS
to the arguments will suppress some of these items. This custom class could be more elaborate, using for example an OrderedDictionary, only noting the order for selected arguments, or using order
to control the display of the attributes.
Another option is to use a custom Action class that creates this order
attribute, e.g.
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)
I adapted the approach from 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')
By adding the "if value:" ... you only get every used argument the correct number of times.
There is a problem with @Martin 's solution: it does not work with cases like this:
parser.add_argument('-s', '--slong', action='store_false')
Here is my solution:
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)
Output is:
OrderedNamespace(_order=['test2', 'test1', 'slong'], slong=False, test1='1', test2='2', test3=3)
1
test2 2
test1 1
slong False
精彩评论