python – How can I pass a list as a command-line argument with argparse?
python – How can I pass a list as a command-line argument with argparse?
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.
Dont use type=list
!!! – There is probably no situation where you would want to use type=list
with argparse
. Ever.
LONG ANSWER
Lets 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 wont 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
cant 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.
- Dont use quotes on the command line1
- Dont 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 dont 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(,)])
python – How can I pass a list as a command-line argument with argparse?
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)