argparse argument order

后端 未结 6 1762
故里飘歌
故里飘歌 2020-12-10 12:03

I have a little problem.

I use argparse to parse my arguments, and it\'s working very well.

To have the args, I do :

p_args = pa         


        
相关标签:
6条回答
  • 2020-12-10 12:03

    This is a bit fragile since it relies on understanding the internals of argparse.ArgumentParser, but in lieu of rewriting argparse.ArgumentParser.parse_known_args, here's what I use:

    class OrderedNamespace(argparse.Namespace):
        def __init__(self, **kwargs):
            self.__dict__["_arg_order"] = []
            self.__dict__["_arg_order_first_time_through"] = True
            argparse.Namespace.__init__(self, **kwargs)
    
        def __setattr__(self, name, value):
            #print("Setting %s -> %s" % (name, value))
            self.__dict__[name] = value
            if name in self._arg_order and hasattr(self, "_arg_order_first_time_through"):
                self.__dict__["_arg_order"] = []
                delattr(self, "_arg_order_first_time_through")
            self.__dict__["_arg_order"].append(name)
    
        def _finalize(self):
            if hasattr(self, "_arg_order_first_time_through"):
                self.__dict__["_arg_order"] = []
                delattr(self, "_arg_order_first_time_through")
    
        def _latest_of(self, k1, k2):
            try:
                print self._arg_order
                if self._arg_order.index(k1) > self._arg_order.index(k2):
                    return k1
            except ValueError:
                if k1 in self._arg_order:
                    return k1
            return k2
    

    This works through the knowledge that argparse.ArgumentParser.parse_known_args runs through the entire option list once setting default values for each argument. Meaning that user specified arguments begin the first time __setattr__ hits an argument that it's seen before.

    Usage:

    options, extra_args = parser.parse_known_args(sys.argv, namespace=OrderedNamespace())
    

    You can check options._arg_order for the order of user specified command line args, or use options._latest_of("arg1", "arg2") to see which of --arg1 or --arg2 was specified later on the command line (which, for my purposes was what I needed: seeing which of two options would be the overriding one).

    UPDATE: had to add _finalize method to handle pathological case of sys.argv() not containing any arguments in the list)

    0 讨论(0)
  • 2020-12-10 12:03

    There is module especially made to handle this :

    https://github.com/claylabs/ordered-keyword-args

    without using orderedkwargs module

    def multiple_kwarguments(first , **lotsofothers):
        print first
    
        for i,other in lotsofothers:
             print other
        return True
    
    multiple_kwarguments("first", second="second", third="third" ,fourth="fourth" ,fifth="fifth")
    

    output:

    first
    second
    fifth
    fourth
    third
    

    On using orderedkwargs module

    from orderedkwargs import ordered kwargs  
    @orderedkwargs  
    def mutliple_kwarguments(first , *lotsofothers):
        print first
    
        for i, other in lotsofothers:
            print other
        return True
    
    
    mutliple_kwarguments("first", second="second", third="third" ,fourth="fourth" ,fifth="fifth")
    

    Output:

    first
    second
    third
    fourth
    fifth
    

    Note: Single asterik is required while using this module with decorator above the function.

    0 讨论(0)
  • 2020-12-10 12:09

    To keep arguments ordered, I use a custom action like this:

    import argparse
    class CustomAction(argparse.Action):
        def __call__(self, parser, namespace, values, option_string=None):
            if not 'ordered_args' in namespace:
                setattr(namespace, 'ordered_args', [])
            previous = namespace.ordered_args
            previous.append((self.dest, values))
            setattr(namespace, 'ordered_args', previous)
    parser = argparse.ArgumentParser()
    parser.add_argument('--test1', action=CustomAction)
    parser.add_argument('--test2', action=CustomAction)
    

    To use it, for example:

    >>> parser.parse_args(['--test2', '2', '--test1', '1'])
    Namespace(ordered_args=[('test2', '2'), ('test1', '1')], test1=None, test2=None)
    
    0 讨论(0)
  • 2020-12-10 12:13

    This is my simple solution based on the existing ones:

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

    OrderedNamespace(_order=['test2', 'test1', 'slong'], slong=False, test1='1', test2='2', test3=3)
    1
    test2 2
    test1 1
    slong False
    

    It allows actions in add_argument(), which is harder for customized action class solution.

    0 讨论(0)
  • 2020-12-10 12:14

    If you need to know the order in which the arguments appear in your parser, you can set up the parser like this:

    import argparse
    
    parser = argparse.ArgumentParser(description = "A cool application.")
    parser.add_argument('--optional1')
    parser.add_argument('positionals', nargs='+')
    parser.add_argument('--optional2')
    
    args = parser.parse_args()
    print args.positionals
    

    Here's a quick example of running this code:

    $ python s.py --optional1 X --optional2 Y 1 2 3 4 5
    ['1', '2', '3', '4', '5']
    

    Note that args.positionals is a list with the positional arguments in order. See the argparse documentation for more information.

    0 讨论(0)
  • 2020-12-10 12:21

    I needed this because, for logging purposes, I liked to print the arguments after they were parsed. The problem was that the arguments are not printed in order, which was really annoying.

    The custom action class just flat out did not work for me. I had other arguments which used a different action such as 'store_true' and default arguments also don't work since the custom action class is not called if the argument is not given in the command line. What worked for me was creating a wrapper class like this:

    import collections
    
    from argparse import ArgumentParser
    
    class SortedArgumentParser():
        def __init__(self, *args, **kwargs):
            self.ap = ArgumentParser(*args, **kwargs)
            self.args_dict = collections.OrderedDict()      
    
        def add_argument(self, *args, **kwargs):
            self.ap.add_argument(*args, **kwargs)
            # Also store dest kwarg
            self.args_dict[kwargs['dest']] = None
    
        def parse_args(self):
            # Returns a sorted dictionary
            unsorted_dict = self.ap.parse_args().__dict__
            for unsorted_entry in unsorted_dict:
                self.args_dict[unsorted_entry] = unsorted_dict[unsorted_entry]
    
            return self.args_dict
    

    The pros are that the add_argument method should have the exact same functionality as the original ArgumentParser. The cons are that if you want other methods you will have to write wrapped for all of them. Luckily for me all I ever used was add_argument and parse_args, so this served my purposes pretty well. You would also need to do more work if you wanted to use parent ArgumentParsers.

    0 讨论(0)
提交回复
热议问题