ChatGPT解决这个技术问题 Extra ChatGPT

Check if argparse optional argument is set or not

I would like to check whether an optional argparse argument has been set by the user or not.

Can I safely check using isset?

Something like this:

if(isset(args.myArg)):
    #do something
else:
    #do something else

Does this work the same for float / int / string type arguments?

I could set a default parameter and check it (e.g., set myArg = -1, or "" for a string, or "NOT_SET"). However, the value I ultimately want to use is only calculated later in the script. So I would be setting it to -1 as a default, and then updating it to something else later. This seems a little clumsy in comparison with simply checking if the value was set by the user.

What would isset() be (hint: Python is not PHP)? Did you mean hasattr() instead, perhaps? Why not configure argparse to set a default for an option instead?
@MartijnPieters - yes, true. So can I simply check if(args.myArg): ...

D
David Jones

I think that optional arguments (specified with --) are initialized to None if they are not supplied. So you can test with is not None. Try the example below:

import argparse

def main():
    parser = argparse.ArgumentParser(description="My Script")
    parser.add_argument("--myArg")
    args, leftovers = parser.parse_known_args()

    if args.myArg is not None:
        print "myArg has been set (value is %s)" % args.myArg

The "is None" and "is not None" tests work exactly as I would like and expect. Thanks.
Unfortunately it doesn't work then the argument got it's default value defined.
If you want to set a default, then you can still set nargs='?' and provide a const value, as described in the docs. When the arg is absent, default is used, when arg given w/o value, then const is used, otherwise given value is used. With only default and nargs='?', default is used if not given, None if given w/o value, otherwise the given value.
@IoannisFilippidis if you use action= "store_true" or action="store_const", const="yourconst" you can't use that argument to store other value. This will not work when using defaults. In my own i've removed all defaults from argparser and handled all inside another function def defaults(): where i do mix ConfigParser, ArgumentParser and default values in the order i want
@erm3nda I didn't mention setting an action. The answer does not use an action. The actions that you mention are documented to behave in one specific way (as you observed). It is not necessary to define an action though.
M
Mateen Ulhaq

As @Honza notes is None is a good test. It's the default default, and the user can't give you a string that duplicates it.

You can specify another default='mydefaultvalue', and test for that. But what if the user specifies that string? Does that count as setting or not?

You can also specify default=argparse.SUPPRESS. Then if the user does not use the argument, it will not appear in the args namespace. But testing that might be more complicated:

parser.add_argument("--foo", default=argparse.SUPPRESS)

# ...

args.foo # raises an AttributeError
hasattr(args, 'foo')  # returns False
getattr(args, 'foo', 'other') # returns 'other'

Internally the parser keeps a list of seen_actions, and uses it for 'required' and 'mutually_exclusive' testing. But it isn't available to you out side of parse_args.


E
Erasmus Cedernaes

I think using the option default=argparse.SUPPRESS makes most sense. Then, instead of checking if the argument is not None, one checks if the argument is in the resulting namespace.

Example:

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("--foo", default=argparse.SUPPRESS)
ns = parser.parse_args()

print("Parsed arguments: {}".format(ns))
print("foo in namespace?: {}".format("foo" in ns))

Usage:

$ python argparse_test.py --foo 1
Parsed arguments: Namespace(foo='1')
foo in namespace?: True
$ python argparse_test.py
Parsed arguments: Namespace()
foo in namespace?: False

This is not working for me under Python 3.7.5 (Anaconda). I get the result testfoo.py: error: argument --foo: expected one argument
@MikeWise If you want to use --foo without a value (i.e. 1 in my example), you have to specify nargs=0 in the add_argument function.
I just copy and pasted your code as specified in the answer. Maybe you should edit it? I ended up using the action='store_true' answer below instead in my actual code.
@MikeWise did you run the script as python argparse_test.py --foo 1?
Does this also work for mutually exclusive groups?
H
Harry Sadler

You can check an optionally passed flag with store_true and store_false argument action options:

import argparse

argparser = argparse.ArgumentParser()
argparser.add_argument('-flag', dest='flag_exists', action='store_true')

print argparser.parse_args([])
# Namespace(flag_exists=False)
print argparser.parse_args(['-flag'])
# Namespace(flag_exists=True)

This way, you don't have to worry about checking by conditional is not None. You simply check for True or False. Read more about these options in the docs here


this doesn't solve to know if an argument that has a value is set or not. the main problem here is to know if the args value comes from defaul="" or it's supplied by user.
y
yPhil

If your argument is positional (ie it doesn't have a "-" or a "--" prefix, just the argument, typically a file name) then you can use the nargs parameter to do this:

parser = argparse.ArgumentParser(description='Foo is a program that does things')
parser.add_argument('filename', nargs='?')
args = parser.parse_args()

if args.filename is not None:
    print('The file name is {}'.format(args.filename))
else:
    print('Oh well ; No args, no problems')

N
NelsonGon

In order to address @kcpr's comment on the (currently accepted) answer by @Honza Osobne

Unfortunately it doesn't work then the argument got it's default value defined.

one can first check if the argument was provided by comparing it with the Namespace object and providing the default=argparse.SUPPRESS option (see @hpaulj's and @Erasmus Cedernaes answers and this python3 doc) and if it hasn't been provided, then set it to a default value.

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--infile', default=argparse.SUPPRESS)
args = parser.parse_args()
if 'infile' in args: 
    # the argument is in the namespace, it's been provided by the user
    # set it to what has been provided
    theinfile = args.infile
    print('argument \'--infile\' was given, set to {}'.format(theinfile))
else:
    # the argument isn't in the namespace
    # set it to a default value
    theinfile = 'your_default.txt'
    print('argument \'--infile\' was not given, set to default {}'.format(theinfile))

Usage

$ python3 testargparse_so.py
argument '--infile' was not given, set to default your_default.txt

$ python3 testargparse_so.py --infile user_file.txt
argument '--infile' was given, set to user_file.txt

A
Adiii

Here is my solution to see if I am using an argparse variable

import argparse

ap = argparse.ArgumentParser()
ap.add_argument("-1", "--first", required=True)
ap.add_argument("-2", "--second", required=True)
ap.add_argument("-3", "--third", required=False) 
# Combine all arguments into a list called args
args = vars(ap.parse_args())
if args["third"] is not None:
# do something

This might give more insight to the above answer which I used and adapted to work for my program.


o
oopsi

Here is a slightly different approach: Suppose you know the argument name, then you can do the following:

import sys

def is_set(arg_name):
    if arg_name in sys.argv:
        return True 
    return False

This way you don't need to change your argument parser in anyway and can still add your custom logic.


It's a good idea, but there's an issue: argparse allows partial matches for optional arguments if there is no ambiguity. (For example if you have a flag --option then --opt will work if there is no other flag that starts with "opt".) You can modify this method to work if you look in sys.argv for an entry that starts with the shortest unique portion of the option, though.
A
Askold Ilvento

A custom action can handle this problem. And I found that it is not so complicated.

is_set = set() #global set reference
class IsStored(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        is_set.add(self.dest) # save to global reference
        setattr(namespace, self.dest + '_set', True) # or you may inject directly to namespace
        setattr(namespace, self.dest, values) # implementation of store_action
        # You cannot inject directly to self.dest until you have a custom class


parser.add_argument("--myarg", type=int, default=1, action=IsStored)
params = parser.parse_args()
print(params.myarg, 'myarg' in is_set)
print(hasattr(params, 'myarg_set'))

h
halfer

Very simple, after defining args variable by 'args = parser.parse_args()' it contains all data of args subset variables too. To check if a variable is set or no assuming the 'action="store_true" is used...

if args.argument_name:
   # do something
else:
   # do something else