SHORT ANSWER
Use the nargs option or the 'append' setting of the action option (depending on how you want the user interface to behave).
nargs
parser.add_argument('-l','--list', nargs='+', help='<Required> Set flag', required=True)
# Use like:
# python arg.py -l 1234 2345 3456 4567
nargs='+' takes 1 or more arguments, nargs='*' takes zero or more.
append
parser.add_argument('-l','--list', action='append', help='<Required> Set flag', required=True)
# Use like:
# python arg.py -l 1234 -l 2345 -l 3456 -l 4567
With append you provide the option multiple times to build up the list.
Don't use type=list!!! - There is probably no situation where you would want to use type=list with argparse. Ever.
LONG ANSWER
Let's take a look in more detail at some of the different ways one might try to do this, and the end result.
import argparse
parser = argparse.ArgumentParser()
# By default it will fail with multiple arguments.
parser.add_argument('--default')
# Telling the type to be a list will also fail for multiple arguments,
# but give incorrect results for a single argument.
parser.add_argument('--list-type', type=list)
# This will allow you to provide multiple arguments, but you will get
# a list of lists which is not desired.
parser.add_argument('--list-type-nargs', type=list, nargs='+')
# This is the correct way to handle accepting multiple arguments.
# '+' == 1 or more.
# '*' == 0 or more.
# '?' == 0 or 1.
# An int is an explicit number of arguments to accept.
parser.add_argument('--nargs', nargs='+')
# To make the input integers
parser.add_argument('--nargs-int-type', nargs='+', type=int)
# An alternate way to accept multiple inputs, but you must
# provide the flag once per input. Of course, you can use
# type=int here if you want.
parser.add_argument('--append-action', action='append')
# To show the results of the given option to screen.
for _, value in parser.parse_args()._get_kwargs():
if value is not None:
print(value)
Here is the output you can expect:
$ python arg.py --default 1234 2345 3456 4567
...
arg.py: error: unrecognized arguments: 2345 3456 4567
$ python arg.py --list-type 1234 2345 3456 4567
...
arg.py: error: unrecognized arguments: 2345 3456 4567
$ # Quotes won't help here...
$ python arg.py --list-type "1234 2345 3456 4567"
['1', '2', '3', '4', ' ', '2', '3', '4', '5', ' ', '3', '4', '5', '6', ' ', '4', '5', '6', '7']
$ python arg.py --list-type-nargs 1234 2345 3456 4567
[['1', '2', '3', '4'], ['2', '3', '4', '5'], ['3', '4', '5', '6'], ['4', '5', '6', '7']]
$ python arg.py --nargs 1234 2345 3456 4567
['1234', '2345', '3456', '4567']
$ python arg.py --nargs-int-type 1234 2345 3456 4567
[1234, 2345, 3456, 4567]
$ # Negative numbers are handled perfectly fine out of the box.
$ python arg.py --nargs-int-type -1234 2345 -3456 4567
[-1234, 2345, -3456, 4567]
$ python arg.py --append-action 1234 --append-action 2345 --append-action 3456 --append-action 4567
['1234', '2345', '3456', '4567']
Takeaways:
- Use
nargsoraction='append'nargscan be more straightforward from a user perspective, but it can be unintuitive if there are positional arguments becauseargparsecan't tell what should be a positional argument and what belongs to thenargs; if you have positional arguments thenaction='append'may end up being a better choice.- The above is only true if
nargsis given'*','+', or'?'. If you provide an integer number (such as4) then there will be no problem mixing options withnargsand positional arguments becauseargparsewill know exactly how many values to expect for the option.
- Don't use quotes on the command line1
- Don't use
type=list, as it will return a list of lists- This happens because under the hood
argparseuses the value oftypeto coerce each individual given argument you your chosentype, not the aggregate of all arguments. - You can use
type=int(or whatever) to get a list of ints (or whatever)
- This happens because under the hood
1: I don't mean in general.. I mean using quotes to pass a list to argparse is not what you want.
SHORT ANSWER
Use the nargs option or the 'append' setting of the action option (depending on how you want the user interface to behave).
nargs
parser.add_argument('-l','--list', nargs='+', help='<Required> Set flag', required=True)
# Use like:
# python arg.py -l 1234 2345 3456 4567
nargs='+' takes 1 or more arguments, nargs='*' takes zero or more.
append
parser.add_argument('-l','--list', action='append', help='<Required> Set flag', required=True)
# Use like:
# python arg.py -l 1234 -l 2345 -l 3456 -l 4567
With append you provide the option multiple times to build up the list.
Don't use type=list!!! - There is probably no situation where you would want to use type=list with argparse. Ever.
LONG ANSWER
Let's take a look in more detail at some of the different ways one might try to do this, and the end result.
import argparse
parser = argparse.ArgumentParser()
# By default it will fail with multiple arguments.
parser.add_argument('--default')
# Telling the type to be a list will also fail for multiple arguments,
# but give incorrect results for a single argument.
parser.add_argument('--list-type', type=list)
# This will allow you to provide multiple arguments, but you will get
# a list of lists which is not desired.
parser.add_argument('--list-type-nargs', type=list, nargs='+')
# This is the correct way to handle accepting multiple arguments.
# '+' == 1 or more.
# '*' == 0 or more.
# '?' == 0 or 1.
# An int is an explicit number of arguments to accept.
parser.add_argument('--nargs', nargs='+')
# To make the input integers
parser.add_argument('--nargs-int-type', nargs='+', type=int)
# An alternate way to accept multiple inputs, but you must
# provide the flag once per input. Of course, you can use
# type=int here if you want.
parser.add_argument('--append-action', action='append')
# To show the results of the given option to screen.
for _, value in parser.parse_args()._get_kwargs():
if value is not None:
print(value)
Here is the output you can expect:
$ python arg.py --default 1234 2345 3456 4567
...
arg.py: error: unrecognized arguments: 2345 3456 4567
$ python arg.py --list-type 1234 2345 3456 4567
...
arg.py: error: unrecognized arguments: 2345 3456 4567
$ # Quotes won't help here...
$ python arg.py --list-type "1234 2345 3456 4567"
['1', '2', '3', '4', ' ', '2', '3', '4', '5', ' ', '3', '4', '5', '6', ' ', '4', '5', '6', '7']
$ python arg.py --list-type-nargs 1234 2345 3456 4567
[['1', '2', '3', '4'], ['2', '3', '4', '5'], ['3', '4', '5', '6'], ['4', '5', '6', '7']]
$ python arg.py --nargs 1234 2345 3456 4567
['1234', '2345', '3456', '4567']
$ python arg.py --nargs-int-type 1234 2345 3456 4567
[1234, 2345, 3456, 4567]
$ # Negative numbers are handled perfectly fine out of the box.
$ python arg.py --nargs-int-type -1234 2345 -3456 4567
[-1234, 2345, -3456, 4567]
$ python arg.py --append-action 1234 --append-action 2345 --append-action 3456 --append-action 4567
['1234', '2345', '3456', '4567']
Takeaways:
- Use
nargsoraction='append'nargscan be more straightforward from a user perspective, but it can be unintuitive if there are positional arguments becauseargparsecan't tell what should be a positional argument and what belongs to thenargs; if you have positional arguments thenaction='append'may end up being a better choice.- The above is only true if
nargsis given'*','+', or'?'. If you provide an integer number (such as4) then there will be no problem mixing options withnargsand positional arguments becauseargparsewill know exactly how many values to expect for the option.
- Don't use quotes on the command line1
- Don't use
type=list, as it will return a list of lists- This happens because under the hood
argparseuses the value oftypeto coerce each individual given argument you your chosentype, not the aggregate of all arguments. - You can use
type=int(or whatever) to get a list of ints (or whatever)
- This happens because under the hood
1: I don't mean in general.. I mean using quotes to pass a list to argparse is not what you want.
I prefer passing a delimited string which I parse later in the script. The reasons for this are; the list can be of any type int or str, and sometimes using nargs I run into problems if there are multiple optional arguments and positional arguments.
parser = ArgumentParser()
parser.add_argument('-l', '--list', help='delimited list input', type=str)
args = parser.parse_args()
my_list = [int(item) for item in args.list.split(',')]
Then,
python test.py -l "265340,268738,270774,270817" [other arguments]
or,
python test.py -l 265340,268738,270774,270817 [other arguments]
will work fine. The delimiter can be a space, too, which would though enforce quotes around the argument value like in the example in the question.
Or you can use a lambda type as suggested in the comments by Chepner:
parser.add_argument('-l', '--list', help='delimited list input',
type=lambda s: [int(item) for item in s.split(',')])
Argparse with arbitrary numbers of arguments but they should occur in pairs
according to the docs you can use the nargs option of .add_argument to gather lists of values.
you'll need to process that list manually like u/mac-reid shows but at least you'll be able to combine it with standard args
here's what I would do:
#!/usr/bin/env python
import sys
import argparse
def dualiter(singleiter):
iterable = iter(singleiter)
while True:
yield next(iterable), next(iterable)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
# ...other args...
parser.add_argument('filepairs', nargs='*')
args = parser.parse_args()
if len(args.filepairs) % 2:
parser.error('filepairs arg should be pairs of values')
for fname, fnum in dualiter(args.filepairs):
print fname, fnum More on reddit.com CLI: populate List with the * wildcard (like argparse nargs='+')
what is the cleanest way to argparse 2-4 names separated by spaces?
How do I pass my own arguments to argparse without using the command line?
I'm wanting to use argparse to parse command-line arguments for a script I'm writing. I want it to be able to specify something on the command-line like the following:
python myscript.py file1 10 file2 5 file3 1 file4 915
for an arbitrary number of files. With each file I want to associate a number (which corresponds to something I will do with the data in that file). I know I can use a single commandline argument with nargs='*' and it will consume all of the commandline arguments at once. However, this seems non-ideal because then I'll have to take the parsed list of arguments and break it up into a list of tuples like
[(file1,10), (file2,5),....]
and I also won't get the parser to be able to check that the argument after each file name is a number. What's the best way to implement this?
Edit: Thanks to everyone for their suggestions!
according to the docs you can use the nargs option of .add_argument to gather lists of values.
you'll need to process that list manually like u/mac-reid shows but at least you'll be able to combine it with standard args
here's what I would do:
#!/usr/bin/env python
import sys
import argparse
def dualiter(singleiter):
iterable = iter(singleiter)
while True:
yield next(iterable), next(iterable)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
# ...other args...
parser.add_argument('filepairs', nargs='*')
args = parser.parse_args()
if len(args.filepairs) % 2:
parser.error('filepairs arg should be pairs of values')
for fname, fnum in dualiter(args.filepairs):
print fname, fnum
How about having your program take myscript.py -f file1 10 -f file2 5 -f file3 1 -f file4 95 instead? This solves both the problems you had.
so I have made a program that takes in a full name as the primary parameter. Usually it is 2 names but sometimes there is 3 or 4. I have been using sys.argv[1:] to just get all the names from the command line, it has been working well but I am now looking into using argparse to add some optional arguments and it is creating some conflicts.
argparse gives an error if I try to use it only for optional args and continue using sys.argv for the positional, I would also have to remove the optional args from the list. I'd prefer to just use argparse but after reading around the docs a bit I am not seeing a way to just get all the positional args. Is there anything that doesn't involve creating 4 positional args or is that just the nature of them?