I want to pass 2 lists of integers as input to a python program.
For e.g, (from command line)
python test.py --a 1 2 3 4 5 -b 1 2
This worked for me:
parser.add_argument('-i', '--ids', help="A comma separated list IDs", type=lambda x: x.split(','))
EDIT:
I have just realised that this doesn't actually answer the question being asked. Jakub has the correct solution.
You can pass them as strings than convert to lists. You can use argparse or optparse.
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--l1', type=str)
parser.add_argument('--l2', type=str)
args = parser.parse_args()
l1_list = args.l1.split(',') # ['1','2','3','4']
Example: python prog.py --l1=1,2,3,4
Also,as a line you can pass something like this 1-50 and then split on '-' and construct range. Something like this:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--l1', type=str, help="two numbers separated by a hyphen")
parser.add_argument('--l2', type=str)
args = parser.parse_args()
l1_list_range = xrange(*args.l1.split('-')) # xrange(1,50)
for i in l1_list_range:
print i
Example: python prog.py --l1=1-50
Logic I think you can write yourself. :)
Just adding this one for completeness. I was surprised that I didn't see this approach.
from argparse import Action, ArgumentParser
class CommaSeparatedListAction(Action):
def __call__(self, parser, namespace, values, option_string=None):
setattr(namespace, self.dest, values.split(','))
parser = ArgumentParser()
parser.add_argument('-l', action=CommaSeparatedListAction)
print(parser.parse_args('-l a,b,c,d'.split()))
# Namespace(l=['a', 'b', 'c', 'd'])
This just a basic example, but you can also add validation or transform values in someway such as coercing them to uppercase.
from argparse import Action, ArgumentParser
class UppercaseLetterCommaSeparatedListAction(Action):
def __call__(self, parser, namespace, values, option_string=None):
letters = values.split(',')
for l in letters:
self._validate_letter(parser, l)
setattr(
namespace,
self.dest,
list(map(lambda v: v.upper(), letters))
)
def _validate_letter(self, parser, letter):
if len(letter) > 1 or not letter.isalpha():
parser.error('l must be a comma separated list of letters')
parser = ArgumentParser()
parser.add_argument('-l', action=UppercaseLetterCommaSeparatedListAction)
print(parser.parse_args('-l a,b,c,d'.split()))
# Namespace(l=['A', 'B', 'C', 'D'])
parser = ArgumentParser()
parser.add_argument('-l', action=UppercaseLetterCommaSeparatedListAction)
print(parser.parse_args('-l a,bb,c,d'.split()))
# usage: list.py [-h] [-l L]
# list.py: error: l must be a comma separated list of letters
parser = ArgumentParser()
parser.add_argument('-l', action=UppercaseLetterCommaSeparatedListAction)
print(parser.parse_args('-l a,1,c,d'.split()))
# usage: list.py [-h] [-l L]
# list.py: error: l must be a comma separated list of letters
If the only arguments are the lists and the separators, you can do it relatively simply:
sa = sys.argv.index('-a')
sb = sys.argv.index('-b')
lista = [int(i) for i in sys.argv[sa+1:sb]]
listb = [int(i) for i in sys.argv[sb+1:]]
Adding validation is easy:
aval = [i for i in lista if i>1 and i<50]
if len(aval) < len(lista):
print 'The -a list contains invalid numbers.'
bval = [i for i in listb if i>1 and i<50]
if len(bval) < len(listb):
print 'The -b list contains invalid numbers.'
Producing a help message:
if sys.argv[1] in ['-h', '-H'] or len(sys.argv) == 1:
print "Usage: <name> -a [list of integers] -b [list of integers]"
The way that optparse and argparse work is they read arguments from the command line, arguments are split by white-space, so if you want to input your list of integers through the command line interfact from optparse or argparse - you can do this by removing the spaces, or by surrounding your argument with "
, example:
> my_script.py --a "1 2 3 4 5" --b "1 2"
or:
> my_script.py --a 1,2,3,4,5 --b 1,2
Your script then needs to convert these inputs into an actual list.
Using argparse syntax (very similar for optparse):
# with spaces and "
a_lst = [i for i in args.a.split(' ')]
b_lst = [i for i in args.b.split(' ')]
# without spaces and ,
a_lst = [i for i in args.a.split(',')]
b_lst = [i for i in args.b.split(',')]
Another way to do this would be by either importing the module you want to run and passing the list objects to a class that deals with your code, or by using a while loop and raw_input/input to collect the desired list.
argparse
supports nargs parameter, which tells you how many parameters it eats.
When nargs="+"
it accepts one or more parameters, so you can pass -b 1 2 3 4
and it will be assigned as a list to b
argument
# args.py
import argparse
p = argparse.ArgumentParser()
# accept two lists of arguments
# like -a 1 2 3 4 -b 1 2 3
p.add_argument('-a', nargs="+", type=int)
p.add_argument('-b', nargs="+", type=int)
args = p.parse_args()
# check if input is valid
set_a = set(args.a)
set_b = set(args.b)
# check if "a" is in proper range.
if len(set_a - set(range(1, 51))) > 0: # can use also min(a)>=1 and max(a)<=50
raise Exception("set a not in range [1,50]")
# check if "b" is in "a"
if len(set_b - set_a) > 0:
raise Exception("set b not entirely in set a")
# you could even skip len(...) and leave just operations on sets
# ...
So you can run:
$ python arg.py -a 1 2 3 4 -b 2 20
Exception: set b not entirely in set a
$ python arg.py -a 1 2 3 4 60 -b 2
Exception: set a not in range [1,50]
And this is valid:
$ python arg.py -a 1 2 3 4 -b 2 3