2017年7月29日 星期六

[ Python 常見問題 ] argparse option for passing a list as option

Source From Here 
Question 
I am trying to pass a list as an argument to a command line program. Is there an argparse option to pass a list as option? 

How-To 
Use the nargs option or the append option (depending on how you want the user interface to behave). 

nargs: The number of command-line arguments that should be consumed. 
For example: 
- test.py 
  1. #!/usr/bin/env python3  
  2. import argparse  
  3.   
  4. parser = argparse.ArgumentParser()  
  5. # nargs='+' takes 1 or more arguments, nargs='*' takes zero or more.  
  6. parser.add_argument('-l','--list', nargs='+', help=' Set flag', required=True)  
  7. args = parser.parse_args()  
  8. print("Given list: {}".format(args.list))  
Execution output: 
# ./test.py -l 1234 2345 3456 4567 
Given list: ['1234', '2345', '3456', '4567']


action(append): This stores a list, and appends each argument value to the list. 
For example: 
- test_append.py 
  1. #!/usr/bin/env python3  
  2. import argparse  
  3.   
  4. parser = argparse.ArgumentParser()  
  5. # This is useful to allow an option to be specified multiple times.  
  6. parser.add_argument('-l','--list', action='append', help=' Set flag', required=True)  
  7. args = parser.parse_args()  
  8. print("Given list: {}".format(args.list))  
Execution output: 
# ./test_append.py -l 1234 -l 2345 -l 3456 -l 4567 
Given list: ['1234', '2345', '3456', '4567']

Notes. Don't use type=list!!! - There is probably no situation where you would want to use type=list with argparse. Ever. 

Let's take a look in more detail at some of the different ways one might try to do this, and the end result. 
- test_all.py 
  1. #!/usr/bin/env python3  
  2. import argparse  
  3.   
  4. parser = argparse.ArgumentParser()  
  5.   
  6. # By default it will fail with multiple arguments.  
  7. parser.add_argument('--default')  
  8.   
  9. # Telling the type to be a list will also fail for multiple arguments,  
  10. # but give incorrect results for a single argument.  
  11. parser.add_argument('--list-type', type=list)  
  12.   
  13. # This will allow you to provide multiple arguments, but you will get  
  14. # a list of lists which is not desired.  
  15. parser.add_argument('--list-type-nargs', type=list, nargs='+')  
  16.   
  17. # This is the correct way to handle accepting multiple arguments.  
  18. # '+' == 1 or more.  
  19. # '*' == 0 or more.  
  20. # '?' == 0 or 1.  
  21. # An int is an explicit number of arguments to accept.  
  22. parser.add_argument('--nargs', nargs='+')  
  23.   
  24. # To make the input integers  
  25. parser.add_argument('--nargs-int-type', nargs='+', type=int)  
  26.   
  27. # An alternate way to accept multiple inputs, but you must  
  28. # provide the flag once per input. Of course, you can use  
  29. # type=int here if you want.  
  30. parser.add_argument('--append-action', action='append')  
  31.   
  32. # To show the results of the given option to screen.  
  33. for _, value in parser.parse_args()._get_kwargs():  
  34.     if value is not None:  
  35.         print(value)  
Here is the output you can expect: 
# ./test_all.py --default 1234 2345 3456 4567 
usage: test_all.py [-h] [--default DEFAULT] [--list-type LIST_TYPE] 
...
 
test_all.py: error: unrecognized arguments: 2345 3456 4567 

# ./test_all.py --list-type 1234 2345 3456 4567 
... 
test_all.py: error: unrecognized arguments: --list-type 1234 2345 3456 4567 

# ./test_all.py --list-type '1234 2345 3456 4567' // Quotes won't help here... 
['1', '2', '3', '4', ' ', '2', '3', '4', '5', ' ', '3', '4', '5', '6', ' ', '4', '5', '6', '7'] 

# ./test_all.py --list-type-nargs 1234 2345 3456 4567 
[['1', '2', '3', '4'], ['2', '3', '4', '5'], ['3', '4', '5', '6'], ['4', '5', '6', '7']] 

# ./test_all.py --nargs 1234 2345 3456 4567 
['1234', '2345', '3456', '4567'] 

# ./test_all.py --nargs-int-type 1234 2345 3456 4567 
[1234, 2345, 3456, 4567] 

# ./test_all.py --nargs-int-type -1234 2345 -3456 4567 // Negative numbers are handled perfectly fine out of the box. 
[-1234, 2345, -3456, 4567] 

# ./test_all.py --append-action 1234 --append-action 2345 --append-action 3456 --append-action 4567 
['1234', '2345', '3456', '4567']

Takeaways: 
- Use nargs or action='append' 
nargs can be more straightforward from a user perspective, but it can be unintuitive if there are positional arguments because argparse can't tell what should be a positional argument and what belongs to the nargs; if you have positional arguments then action='append' may end up being a better choice. The above is only true if nargs is given '*', '+', or '?'. If you provide an integer number (such as 4) then there will be no problem mixing options with nargs and positional arguments because argparse will know exactly how many values to expect for the option.

- Don't use type=list, as it will return a list of lists 
For request here, this option is not useful here. The unexpected result happens because under the hood argparse uses the value of type to coerce each individual given argument to your chosen type, not to aggregate of all arguments. Instead, You can use type=int (or whatever) to get a list of integers.


沒有留言:

張貼留言

[Git 文章收集] Differences between git merge and git rebase

Source From  Here Preface Merging and rebasing are the two most popular way to applying changes from one branch into another one. They bot...