AltoBalto
AltoBalto

Reputation: 401

Pythons argparse default value doesn't work

I'm using python 2.7.13.

My goal is to have three possible arguments, with default values being set if no arguments are given by the user:

parser.add_argument("-r", nargs=3, default=(0, 1000, 50), type=int, help="Useful help text")

This doesn't work for me, and I can't find anywhere if it is possible to use default in such a way as above.

When running it as program.py -r I get a an error: expected 3 argument(s)

But I also tried removing nargs completely and only having one default value:

parser.add_argument("-r", default=100)

Strangely enough, this doesn't work either. It requires at least one argument...

Anyone understand this?

Upvotes: 3

Views: 6293

Answers (4)

hpaulj
hpaulj

Reputation: 231395

I'll illustrate the normal behavior of default in argparse (with a Ipython interactive session)

In [32]: parser = argparse.ArgumentParser()

Define 3 Actions:

In [33]: parser.add_argument('-r', nargs=3, type=int, default=(1,2,3));
In [35]: parser.add_argument('-f', default='DEFAULT');
In [37]: parser.add_argument('-g', nargs='?', default='DEFAULT', const='const');

The help. Note that all Actions have [], indicating that they are optional:

In [39]: parser.print_help()
usage: ipython3 [-h] [-r R R R] [-f F] [-g [G]]

optional arguments:
  -h, --help  show this help message and exit
  -r R R R
  -f F
  -g [G]

If called without any argments, all of the defaults appear in the args namespace.

In [40]: parser.parse_args([])  # e.g python myprog.py
Out[40]: Namespace(f='DEFAULT', g='DEFAULT', r=(1, 2, 3))

Giving -r with 3 numbers (as specified by the nargs)

In [41]: parser.parse_args('-r 4 5 6'.split())  
Out[41]: Namespace(f='DEFAULT', g='DEFAULT', r=[4, 5, 6])

Specify one of the other flags. Note the remaining defaults

In [42]: parser.parse_args('-f other'.split())  
Out[42]: Namespace(f='other', g='DEFAULT', r=(1, 2, 3))

-g with nargs='?' has another option. It can be given without arguments. In that case it gets the const value.

In [43]: parser.parse_args('-f other -g'.split())  
Out[43]: Namespace(f='other', g='const', r=(1, 2, 3))
In [44]: parser.parse_args('-f other -g more'.split())  
Out[44]: Namespace(f='other', g='more', r=(1, 2, 3))

There isn't such a 3 way option for nargs=3. You either provide the 3 values, or you don't use -r. If you need to distinguish between 1) no-r flag, 2) r flag without arguments, and 3) r flat with 3 arguments, I'd suggest splitting functionality into 2 actions, one a 'store_true', and other that takes the 3 values.

Defaults in argparse can be a complicated matter, with various ways of setting them, differences between string and non-string values, even a way of suppressing them. But I've shown the basic behavior.

Upvotes: 4

Lukisn
Lukisn

Reputation: 190

With your example you can either call the application without the -r flag which would result in the default values. If you want to specify the values yourself you have to give them all.

If you would like to enter them independently you could try something like this:

# script.py
import argparse

def main1():
    parser = argparse.ArgumentParser()
    parser.add_argument("-r", nargs=3, default=(0, 1000, 50), type=int,
                        help="Useful help text")
    args = parser.parse_args()
    print(args)

def main2():
    parser = argparse.ArgumentParser()
    parser.add_argument("-r1", type=int, default=0, help="...")
    parser.add_argument("-r2", type=int, default=1000, help="...")
    parser.add_argument("-r3", type=int, default=50, help="...")
    args = parser.parse_args()
    print(args)

if __name__ == "__main__":
    main1()
    #main2()

You could call your implementation (main1) like this:

$ python script.py
Namespace(r=(0, 1000, 50))
$ python script.py -r 1 2 3
Namespace(r=[1, 2, 3])
$ python script.py -r 1
usage: args.py [-h] [-r R R R]
args.py: error: argument -r: expected 3 arguments

Using separate arguments your invocations could be looking like this (main2):

$ python script.py -r1 1 -r2 2 -r3 3
Namespace(r1=1, r2=2, r3=3)
$ python script.py -r1 1
Namespace(r1=1, r2=1000, r3=50)
$ python script.py -r2 2
Namespace(r1=0, r2=2, r3=50)
$ python script.py -r3 3
Namespace(r1=0, r2=1000, r3=3)

This could be a little bit more wordy if you want to change all values but it gives you more flexibility. I think you could even combine those two approaches using a mutually_exclusive_group.

Upvotes: 0

user2261062
user2261062

Reputation:

Why don't you use argv?

Example:

import sys

def main(first=1, second=2, third=3):
    print first,second,third

if __name__ == '__main__':
    main(*sys.argv[1:])       # notice the * before sys.argv[1:]

You can test executing the file from console with different number of parameters:

python myprogram.py          # will print 1 2 3
python myprogram.py 7        # will print 7 2 3
python myprogram.py 7 8      # will print 7 8 3
python myprogram.py 7 8 9    # will print 7 8 9

Upvotes: 0

user5725006
user5725006

Reputation:

You only need to call the argument -r if you are not using the default value. If you do call then you have to also pass a value (or as many values you define in nargs).

Try just calling program.py

Upvotes: 1

Related Questions