837

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?

parser.add_argument('-l', '--list',
 type=list, action='store',
 dest='list',
 help='<Required> Set flag',
 required=True)

Script is called like below

python test.py -l "265340 268738 270774 270817"
nbro
16k34 gold badges122 silver badges219 bronze badges
asked Apr 1, 2013 at 23:34
0

13 Answers 13

1686

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 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 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 of type to coerce each individual given argument you your chosen type, not the aggregate of all arguments.
    • You can use type=int (or whatever) to get a list of ints (or whatever)

1: I don't mean in general.. I mean using quotes to pass a list to argparse is not what you want.

Mateen Ulhaq
27.9k21 gold badges122 silver badges155 bronze badges
answered Apr 1, 2013 at 23:37
Sign up to request clarification or add additional context in comments.

18 Comments

What about a list of strings? This turns multiple string arguments ("wassup", "something", and "else")into a list of lists that looks like this: [['w', 'a', 's', 's', 'u', 'p'], ['s', 'o', 'm', 'e', 't', 'h', 'i', 'n', 'g'], ['e', 'l', 's', 'e']]
@rd108 I see, I bet that you are using the type=list option. Don't use that. That turns a string into a list, and hence the lists of lists.
@Dror All input is assumed to be strings unless you set the type parameter to some other object. By default this method returns a list of strings.
-- could split options vs. positional arguments. prog --opt1 par1 ... -- posp1 posp2 ...
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. -- helps to figure this out as shown in example in my previous comment. IOW user supplies -- followed by all positional arguments.
|
157

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(',')])
Roelant
5,2195 gold badges43 silver badges77 bronze badges
answered Jul 21, 2014 at 13:55

13 Comments

You can set the type argument to lambda s: [int(time) for item in s.split(',')] instead of post-processing args.list.
@chepner,yes you're absolutely right and it would be more pythonic - just a small typo: int(time) should be int(item). My example was a simplified version of what I typically do, where I check many other things rather than a simple processing. But to simply answer the question, I too find your way more elegant..
this answer looks to be the most pythonic
The comment by @chepner is some serious ninja skillz +1
lambda items: list(csv.reader([items]))[0] with the standard csv library is a modified version of the comment from @chepner for anyone worried about arbitrary CSV input (ref: answer from @adamk).
|
22

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')
answered Mar 27, 2014 at 11:52

2 Comments

How I can use nargs with choices? Can you show the combined example?
Say I want to allow the user to make 1 or more choices, like game.py --move=rock,paper?
22

JSON List Solution

A nice way to pass lists (and dicts) in via the command line is by using json.

Just specify type=json.loads:

# parse_list.py
import argparse
import json
parser = argparse.ArgumentParser()
parser.add_argument('-l', '--list', type=json.loads)
args = parser.parse_args()
print(args.list)

Example Usage

$ python parse_list.py -l "[265340, 268738, 270774, 270817]"
[265340, 268738, 270774, 270817]

Edit: incorporated the improvement suggested by Katu to remove the separate parsing step.

The json parsing can be handled separately to avoid misusing the type arg:

# parse_list.py
import argparse
import json
parser = argparse.ArgumentParser()
parser.add_argument('-l', '--list', type=str)
args = parser.parse_args()
# parse string json input to python list
parsed_list = json.loads(args.list)
print(parsed_list)
answered Apr 21, 2021 at 17:40

1 Comment

Do note the warning from the docs: "... Anything with more interesting error-handling or resource management should be done downstream after the arguments are parsed. For example, JSON or YAML conversions have complex error cases that require better reporting than can be given by the type keyword. ..."
20

Using nargs parameter in argparse's add_argument method

I use nargs='*' as an add_argument parameter. I specifically used nargs='*' to the option to pick defaults if I am not passing any explicit arguments

Including a code snippet as example:

Example: temp_args1.py

Please Note: The below sample code is written in python3. By changing the print statement format, can run in python2

#!/usr/local/bin/python3.6
from argparse import ArgumentParser
description = 'testing for passing multiple arguments and to get list of args'
parser = ArgumentParser(description=description)
parser.add_argument('-i', '--item', action='store', dest='alist',
 type=str, nargs='*', default=['item1', 'item2', 'item3'],
 help="Examples: -i item1 item2, -i item3")
opts = parser.parse_args()
print("List of items: {}".format(opts.alist))

Note: I am collecting multiple string arguments that gets stored in the list - opts.alist If you want list of integers, change the type parameter on parser.add_argument to int

Execution Result:

python3.6 temp_agrs1.py -i item5 item6 item7
List of items: ['item5', 'item6', 'item7']
python3.6 temp_agrs1.py -i item10
List of items: ['item10']
python3.6 temp_agrs1.py
List of items: ['item1', 'item2', 'item3']
LudvigH
4,9745 gold badges39 silver badges60 bronze badges
answered Jan 20, 2017 at 20:30

4 Comments

@Py_minion Is there a way to use a list as an argument, and have the output as list as well? temp_args1.py -i [item5 ,item6, item7] and have the output come out as a list as well (instead of nested list)
@Moondra Yes. glad you asked. ``` parser.add_argument('-o', '--options', action='store', dest='opt_list', type=str, nargs='*', default=sample_list, help="String of databases seperated by white space. Examples: \ -o option1 option2, -o option3") ``` Here 'sample_list' is of type list with default options. Ex: sample_list = [option4, option5]
@Py_minion Thank you. Going to test it out later today.
I used this, this is very useful for passing creating lists from the arguments.
10

If you are intending to make a single switch take multiple parameters, then you use nargs='+'. If your example '-l' is actually taking integers:

a = argparse.ArgumentParser()
a.add_argument(
 '-l', '--list', # either of this switches
 nargs='+', # one or more parameters to this switch
 type=int, # /parameters/ are ints
 dest='lst', # store in 'lst'.
 default=[], # since we're not specifying required.
)
print a.parse_args("-l 123 234 345 456".split(' '))
print a.parse_args("-l 123 -l=234 -l345 --list 456".split(' '))

Produces

Namespace(lst=[123, 234, 345, 456])
Namespace(lst=[456]) # Attention!

If you specify the same argument multiple times, the default action ('store') replaces the existing data.

The alternative is to use the append action:

a = argparse.ArgumentParser()
a.add_argument(
 '-l', '--list', # either of this switches
 type=int, # /parameters/ are ints
 dest='lst', # store in 'lst'.
 default=[], # since we're not specifying required.
 action='append', # add to the list instead of replacing it
)
print a.parse_args("-l 123 -l=234 -l345 --list 456".split(' '))

Which produces

Namespace(lst=[123, 234, 345, 456])

Or you can write a custom handler/action to parse comma-separated values so that you could do

-l 123,234,345 -l 456
qneill
1,74315 silver badges19 bronze badges
answered Mar 31, 2017 at 16:45

Comments

7

In add_argument(), type is just a callable object that receives string and returns option value.

import ast
def arg_as_list(s): 
 v = ast.literal_eval(s) 
 if type(v) is not list: 
 raise argparse.ArgumentTypeError("Argument \"%s\" is not a list" % (s))
 return v 
def foo():
 parser.add_argument("--list", type=arg_as_list, default=[],
 help="List of values")

This will allow to:

$ ./tool --list "[1,2,3,4]"
answered Nov 10, 2017 at 17:04

1 Comment

Note that if one needed to pass strings, this method would require they quote them appropriately on the command line. A user may find this unexpected. If only parsing integers this is fine.
5

I think the most elegant solution is to pass a lambda function to "type", as mentioned by Chepner. In addition to this, if you do not know beforehand what the delimiter of your list will be, you can also pass multiple delimiters to re.split:

# python3 test.py -l "abc xyz, 123"
import re
import argparse
parser = argparse.ArgumentParser(description='Process a list.')
parser.add_argument('-l', '--list',
 type=lambda s: re.split(' |, ', s),
 required=True,
 help='comma or space delimited list of characters')
args = parser.parse_args()
print(args.list)
# Output: ['abc', 'xyz', '123']
answered Apr 14, 2020 at 10:43

3 Comments

Did you mean -l in the example call? Where did -n come from?
Also, the solution doesn't work for me in Python 3.8.2. Here is the code: parser.add_argument('-l', '--list', type = lambda s: re.split('[ ,;]', s)). Here is the input: script.py -l abc xyz, abc\nxyz. Finally, here is the result: script.py: error: unrecognized arguments: xyz, abcnxyz
Fixed the solution according to the comments!
3

Do be advised that if you pass action='append' along with the default argument, Argparse will attempt to append to supplied default values rather than replacing the default value, which you may or may not expect.

Here is one action='append example given in the Argparse Docs. In this case things will work as expected:

>> import argparse
>> parser = argparse.ArgumentParser()
>> parser.add_argument('--foo', action='append')
>> parser.parse_args('--foo 1 --foo 2'.split())
Out[2]: Namespace(foo=['1', '2'])

However, if you opt to provide a default value, Argparse's "append" action will attempt to append to the supplied defaults, rather than replacing the default values:

import argparse
REASONABLE_DEFAULTS = ['3', '4']
parser = argparse.ArgumentParser()
parser.add_argument('--foo', default=REASONABLE_DEFAULTS,action='append')
parser.parse_args('--foo 1 --foo 2'.split())
Out[6]: Namespace(foo=['3', '4', '1', '2'])

If you were expecting Argparse to replace the default values -- such as passing in a tuple as a default, rather than a list -- this can lead to some confusing errors:

import argparse
REASONABLE_DEFAULTS = ('3', '4')
parser = argparse.ArgumentParser()
parser.add_argument('--foo', default=REASONABLE_DEFAULTS,action='append')
parser.parse_args('--foo 1 --foo 2'.split())
AttributeError: 'tuple' object has no attribute 'append'

There is a bug tracking this unexpected behavior, but since it dates from 2012, it's not likely to get resolved.

answered Feb 18, 2022 at 1:37

Comments

2

If you have a nested list where the inner lists have different types and lengths and you would like to preserve the type, e.g.,

[[1, 2], ["foo", "bar"], [3.14, "baz", 20]]

then you can use the solution proposed by @sam-mason to this question, shown below:

from argparse import ArgumentParser
import json
parser = ArgumentParser()
parser.add_argument('-l', type=json.loads)
parser.parse_args(['-l', '[[1,2],["foo","bar"],[3.14,"baz",20]]'])

which gives:

Namespace(l=[[1, 2], ['foo', 'bar'], [3.14, 'baz', 20]])
answered Jul 19, 2019 at 13:14

Comments

1

You can parse the list as a string and use of the eval builtin function to read it as a list. In this case, you will have to put single quotes into double quote (or the way around) in order to ensure successful string parse.

# declare the list arg as a string
parser.add_argument('-l', '--list', type=str)
# parse
args = parser.parse()
# turn the 'list' string argument into a list object
args.list = eval(args.list)
print(list)
print(type(list))

Testing:

python list_arg.py --list "[1, 2, 3]"
[1, 2, 3]
<class 'list'>
answered Sep 25, 2020 at 6:40

Comments

1

Applying chepner's comment to Lunguini's answer:

import argparse, json 
parser = argparse.ArgumentParser() 
parser.add_argument('-l', '--list', type=lambda a: json.loads('['+a.replace(" ",",")+']'), default="", help="List of values") 
args = parser.parse_args() 
print(args.list) 

Usage:

$ python parse_list.py -l "265340 268738 270774 270817"
[265340, 268738, 270774, 270817]
answered Aug 11, 2022 at 9:04

Comments

0

I want to handle passing multiple lists, integer values and strings.

Helpful link => How to pass a Bash variable to Python?

def main(args):
 my_args = []
 for arg in args:
 if arg.startswith("[") and arg.endswith("]"):
 arg = arg.replace("[", "").replace("]", "")
 my_args.append(arg.split(","))
 else:
 my_args.append(arg)
 print(my_args)
if __name__ == "__main__":
 import sys
 main(sys.argv[1:])

Order is not important. If you want to pass a list just do as in between "[" and "] and seperate them using a comma.

Then,

python test.py my_string 3 "[1,2]" "[3,4,5]"

Output => ['my_string', '3', ['1', '2'], ['3', '4', '5']], my_args variable contains the arguments in order.

answered Mar 16, 2020 at 17:12

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.