How can I pass a list as a command-line argument with argparse?
TL;DR
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.
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 argparseparser = 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 integersparser.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
nargs
oraction='append'
nargs
can be more straightforward from a user perspective, but it can be unintuitive if there are positional arguments becauseargparse
can'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
nargs
is given'*'
,'+'
, or'?'
. If you provide an integer number (such as4
) then there will be no problem mixing options withnargs
and positional arguments becauseargparse
will 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
argparse
uses the value oftype
to 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(',')])
Additionally to nargs
, you might want to use choices
if you know the list in advance:
>>> parser = argparse.ArgumentParser(prog='game.py')>>> parser.add_argument('move', choices=['rock', 'paper', 'scissors'])>>> parser.parse_args(['rock'])Namespace(move='rock')>>> parser.parse_args(['fire'])usage: game.py [-h] {rock,paper,scissors}game.py: error: argument move: invalid choice: 'fire' (choose from 'rock','paper', 'scissors')