Reputation: 425
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
The integers in this list can range from 1-50, List 2 is subset of List1.
Any help/suggestions ? Is argparse
the right module ? Any concerns in using that ?
I have tried :
import argparse
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--a', help='Enter list 1 ')
parser.add_argument('--b', help='Enter list 2 ')
args = parser.parse_args()
print (args.a)
Upvotes: 41
Views: 56905
Reputation: 71
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.
Upvotes: 7
Reputation: 1807
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
Upvotes: 1
Reputation: 33827
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
Upvotes: 87
Reputation: 1790
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. :)
Upvotes: 16
Reputation: 43495
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]"
Upvotes: 1
Reputation: 43447
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.
Upvotes: 1