43

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)
5
  • How does the list of integers look like? Commented Mar 17, 2013 at 11:08
  • Show a sample commandline you are going to use? Also, add common comments as part of the question. Commented Mar 17, 2013 at 11:11
  • integers in the each list range from 1 to 50. List 2 is subset of list1 Commented Mar 17, 2013 at 11:11
  • Space-separated values are different arguments by convention. Either quote your lists ("1 2 3 4 5") or comma-separate them (1,2,3,4,5) -- then argparse will treat them as one argument. Commented Mar 17, 2013 at 11:41
  • @katrielalex: nargs="+" changes this behaviour, so it eats -a 1 2 3 as a single list Commented Mar 17, 2013 at 11:54

6 Answers 6

90

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
Sign up to request clarification or add additional context in comments.

6 Comments

good decision! true if it is necessary to list the 50 numbers, not 5, it is long))
Because what you ask for is set checking. When you check if B is in A, it is set operation - check if set b - set a is empty. For ranges, I could use min(a) >= 1 and max(a) <= 50, although I wanted to be consistent. You asked about "List 2 is subset of List1.", no?
For validity checking, why not use if all(1 <= a <= 50 for a in set_a) ?
@InbarRose: nice one, if I refactored it, I would use it
How do I set a default list of values in this case?
|
16

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. :)

4 Comments

Any sample code to do that ? I also want to throw error if any list is empty or contains the number out of range ?
Please bring a sample list that you want to pass.
@Swati StackOverflow is not a code writing service. Please make an attempt yourself at the problem and then ask for help with your solution.
sample list list1 contains number 1-50 and list2 contains subset of number from list1
7

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.

Comments

1

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.

Comments

1

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]"

1 Comment

I like your approach, how do I provide help regarding the input parameters and any validation about the range ?
1

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

Comments

Your Answer

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.