Passing an optional argument in a function - python

30,048

Solution 1

This is explained in the FineManual(tm): https://docs.python.org/2/tutorial/controlflow.html#more-on-defining-functions

Note that in Python, the expression defining the default value for an optional argument is eval'd ony once when the def statement is executed (which is at first import for a top-level function), which can lead to unexpected behaviours (cf "Least Astonishment" and the Mutable Default Argument).

Also, the "default value" has to be an expression, not a statement, so you cannot do any error handling here. wrt/ your case with trying to use sys.argv[2] as a default value, it's wrong for at least two reasons:

  1. as you already noticed, it breaks if len(sys.argv) < 3
  2. it makes your function dependent on sys.argv, so you cannot reuse it in a different context

The right solution here is to handle all user input (sys.argv or whatever) in the "entry point" code (the __main__ section) - your function should know nothing about where the arguments values came from (sys.argv, an HTTP request, a text file or whatever).

So to make a long story short: use either a hardcoded value (if it makes sense) or a "sentinel" value (None is a good candidate) as default value for your optional argument, and do all the user inputs parsing in the __main__ section (or even better in a main() function called from the __main__ section so you don't pollute the module's namespace with irrelevant variables):

def func(arg, optarg=None):
    #code here


def main(*args):
    #parse args
    #call func with the right args

if __name__ == "__main__":
    import sys 
    main(*sys.argv)

Solution 2

You can write your function by providing default argument value to the argument you want to ignore like optionalArg=None(whatever you want) by doing this you can call the function with single argument.

Share:
30,048
Kfir Cohen
Author by

Kfir Cohen

Updated on February 08, 2020

Comments

  • Kfir Cohen
    Kfir Cohen over 4 years

    I would like to create a function which can take either 1 or 2 arguments. Currently, I have a function which takes exactly 2 arguments through CMD:

    def test(self,countName,optionalArg):
            if countName == "lowest":
               #something
            if optionalArg == "furthest:
               #something
            else:
               #something else
    
    if __name__ == '__main__':
            countName = sys.argv[1]
            optionalArg = sys.argv[2]
    
            temp = len(sys.argv)
            for i in xrange(1,temp):
    
                sys.argv.pop()
    

    I would then run:

    python filename.py lowest furthest

    Using this means that passing the second arg is a must. If I try to run my script just by passing one arg, it encounters an error (as expected). My question is, how do you create an optional argument, which could either be passed or not, depending on the situation?

    For example:

    python filename.py lowest

    In this situation, I expect the program to perform the "#something else" script, as nothing was passed and it is different than "furthest".

    Please do not write the code for me, I am here to learn :)

    • Christofer Ohlsson
      Christofer Ohlsson over 8 years
      Consider having your function accept one argument, that is in itself a list of arguments. That way you can do different things, depending on the length of that list.
    • Kfir Cohen
      Kfir Cohen over 8 years
      @ChristoferOhlsson Hi Christoper, if I change the function to accept only one argument, then it disregards the things I am writing through CMD, thus not letting me pass a second argument.
    • Christofer Ohlsson
      Christofer Ohlsson over 8 years
      If you keep passing it two arguments, then sure. But why would you?
    • Kfir Cohen
      Kfir Cohen over 8 years
      @ChristoferOhlsson Because I would like to perform different calculations. If "furthest" was passed, then do something, but if nothing was passed, do something else. I could pass "zzz" through CMD to not receive the error and make it work, however, this seems like a very ugly solution.
    • DainDwarf
      DainDwarf over 8 years
      If you plan on using different options on command-line, some of which are optionals, with possibly different types of input, you should read about the argparse module.
    • Christofer Ohlsson
      Christofer Ohlsson over 8 years
      Which works just fine with my suggestion. You do pretty much what I'm suggesting with the argv list.
    • PenguinEngineer
      PenguinEngineer over 6 years
  • Kfir Cohen
    Kfir Cohen over 8 years
    Hi, thank you for your help. I have read it and couldn't quite figure out how to resolve my issue using this. I have updated my question, would appreciate if you could review it. I believe my issue relies in "optionalArg = sys.argv[2]", as it is always expecting a statement there. not passing anything through the CMD gives a "list index out of range" error.
  • Kfir Cohen
    Kfir Cohen over 8 years
    I'm terribly sorry it took me so much time to respond, personal issues. Thank you very much, this greatly assisted me! I managed to convert my code and use the (*args) in the way mentioned. One thing is still unclear to me, and that is how do I check the args that were passed. I can't use 'if args[1]' since it also breaks the code, if no args were passed. Then how am I supposed to check what arg was actually passed?
  • bruno desthuilliers
    bruno desthuilliers over 8 years
    When using def foo(*args), in foo, args will be a tuple. The very first thing to do is of course to check it's length and take appropriate action if no args were actually passed. Also note that there are a couple command line arguments parsers in the standard lib...
  • Kfir Cohen
    Kfir Cohen over 8 years
    Perfect. Thank you very much!