How do I define a function with optional arguments?
Asked Answered
A

8

441

I have a Python function which takes several arguments. Some of these arguments could be omitted in some scenarios.

def some_function (self, a, b, c, d = None, e = None, f = None, g = None, h = None):
    #code

The arguments d through h are strings which each have different meanings. It is important that I can choose which optional parameters to pass in any combination. For example, (a, b, C, d, e), or (a, b, C, g, h), or (a, b, C, d, e, f, or all of them (these are my choices).

It would be great if I could overload the function - but I read that Python does not support overloading. I tried to insert some of the required int arguments in the list - and got an argument mismatch error.

Right now I am sending empty strings in place of the first few missing arguments as placeholders. I would like to be able to call a function just using actual values.

Is there any way to do this? Could I pass a list instead of the argument list?

Right now the prototype using ctypes looks something like:

_fdll.some_function.argtypes = [c_void_p, c_char_p, c_int, c_char_p, c_char_p, c_char_p, c_char_p, c_char_p]
Athodyd answered 2/3, 2012 at 20:30 Comment(1)
M
322

Try calling it like: obj.some_function( '1', 2, '3', g="foo", h="bar" ). After the required positional arguments, you can specify specific optional arguments by name.

Mauretta answered 2/3, 2012 at 20:32 Comment(0)
M
448

Just use the *args parameter, which allows you to pass as many arguments as you want after your a,b,c. You would have to add some logic to map args->c,d,e,f but its a "way" of overloading.

def myfunc(a,b, *args, **kwargs):
   for ar in args:
      print ar
myfunc(a,b,c,d,e,f)

And it will print values of c,d,e,f


Similarly you could use the kwargs argument and then you could name your parameters.

def myfunc(a,b, *args, **kwargs):
      c = kwargs.get('c', None)
      d = kwargs.get('d', None)
      #etc
myfunc(a,b, c='nick', d='dog', ...)

And then kwargs would have a dictionary of all the parameters that are key valued after a,b

Morrow answered 2/3, 2012 at 20:35 Comment(5)
Thank you. While I can't use this much flexibility in my code, and it doesn't solve my problem of calling with various empty holes in my list, it is a great tool to be used in a different project. and Russel Borogove gave me the exact answer I needed for my problem, I am happy.Athodyd
It is called Arbitrary Argument ListsNelsen
@Nix, I really like your answer. However, could you please clarify what the statement c = kwargs.get('c', None) is used for? Is it required in every functions that have optional args? In your code, you only had 2 optional args. What if user wants another arg say e? How can I alter your sample code for any unknown number of optional args, for ex: def my_func(a, b, *args, **kwagars): obj = <do something with a & b> obj.add(c) obj.add(d) continue obj.add(for e, f, g...)? Do I have to include c = kwargs.get('c', None) etc before doing the obj.add(c, d, ...)?Hardpressed
If you are worried about time complexity. This would be an issueHumism
c = kwargs.get('c', None) should be c = kwargs.get('c') as None is default value if key is not present.Homeopathy
M
322

Try calling it like: obj.some_function( '1', 2, '3', g="foo", h="bar" ). After the required positional arguments, you can specify specific optional arguments by name.

Mauretta answered 2/3, 2012 at 20:32 Comment(0)
M
90

It is quite easy

def foo(a = None):
       print(a)

"None" can be replaced by any other default value.

For example in case no arguments were given to the function foo() then what do you think it will print? Answer is "None." Why? because no arguments were given and the default value was "None"

But, if you will give it an argument such as foo("hello world") then....drum rolling... it will print "hello world."

There's one more thing you should remember which I apparently forgot to tell. These "optional parameters" need to be behind all the other parameters. This means that, let's take the previous function and add another parameter b

#def foo(a = None, b): 
#    print("Value of a is", a, " and value of b is", b)

def foo(a, b=None): 
    print("Value of a is", a, " and value of b is", b)

The first function (the commented one) will generate an error because the optional parameter "b" was after the required parameter "a." But the second definition would definitely work.

So, you have to put the optional parameters after the ones which are required.

Maynard answered 15/4, 2021 at 6:36 Comment(0)
V
27

Required parameters first, optional parameters after. Optional parameters always with a =None.

Easy and fast example:

def example_function(param1, param2, param3=None, param4=None):
    pass

# Doesn't work, param2 missing
example_function("hello")

# Works
example_function("hello", "bye")

# Works. Both the same
example_function("hello", "bye", "hey")
example_function("hello", "bye", param3="hey")

# Works. Both the same
example_function("hello", "bye", "hey", "foo")
example_function("hello", "bye", param3="hey", param4="foo")
Vertical answered 14/1, 2022 at 12:26 Comment(2)
** Required parameters first, optional parameters after. ** That is the key.Britney
Does this work with int or float? It accepted argument but then I wanted to define operations if the argument was passed and then it doesn't seem to work. For eg, if I want to check if the length of param3 is 3, then I would put a assert statement but if the param3 is not passed, this throws error because len wouldn't work on None. So I cannot validate or use my argument inside the function effectively. My parameters are floats and I want to carry arithmetic and operations are not defined for NoneUniversity
E
16

Check this:

from typing import Optional

def foo(a: str, b: Optional[str] = None) -> str or None:
    pass
Electrostatic answered 17/11, 2021 at 10:12 Comment(5)
But these annotations aren't used during runtime to enforce or even check parameter types. So this isn't the proper way, it actually doesn't do what the OP wants.Binette
@TonySuffolk66 so optional is just an indication that you are allowed to put None inside the parameter, but it won't have a None value if ommited?Smegma
These two aren't equivalent: * In the first example, 'b' is still a positional argument that must be provided: * in the second example, 'b' is a true optional argument that can be ommitted (and in this case will have the value None if ommitted). The thing that makes the argument truely optional is the '= <value>' in the parameter list - the Optional[...] annotation is irrelevant as to whether the argument is positional or optional.Binette
These two are NOT equivalent. as @TonySuffolk66 commented.Gonfalonier
@Smegma : Using Optional just indicates to a static analyzer that the argument can be None or in this case a string. The Optional[ ...] mark does absolutely nothing at run time.Binette
D
1

To get a better sense of what's possible when passing parameters it's really helpful to refer to the various options: positional-or-keyword (arg or arg="default_value"), positional-only (before /, in the parameter list), keyword-only (after *, in the parameter list), var-positional (typically *args) or var-keyword (typically **kwargs). See the Python documentation for an excellent summary; the various other answers to the question make use of most of these variations.

Since you always have parameters a, b, c in your example and you appear to call them in a positional manner, you could make this more explicit by adding /,,

def some_function (self, a, b, c, /, d = None, e = None, f = None, g = None, h = None):
    #code
Danzig answered 4/3, 2022 at 13:17 Comment(0)
B
1

To make Avión's answer work for vector argument inputs;

def test(M,v=None):
    try: 
        if (v==None).all() == False:
            print('argument passed')
            return M + v 
    except: 
        print('no argument passed')
        return M 

Where M is some matrix and v some vector. Both test(M) and test(M,v) produce errors when I attempted to use if statements without using 'try/ except' statements.

As mentioned by cem, upgrading to python 3.10 would allow the union (x|y) (or the Optional[...])functionality which might open some doors for alternative methods, but I'm using Anaconda spyder so I think I have to wait for a new release to use python 3.10.

Blague answered 6/6, 2022 at 0:38 Comment(0)
T
0

A Python function can take in some arguments, take this for example,

def add(x,y):
    return x+ y

# calling this will require only x and y
add(2,3) # 5

If we want to add as many arguments as we may want, we shall just use *args which shall be a list of more arguments than the number of formal arguments that you previously defined (x and y). With *args, any number of extra arguments can be attached to your current formal parameters.

def add(x, y, *args):
    return x + y + sum(args)
    
add(1,2,3,4,5,6) # 21
Trouveur answered 28/12, 2023 at 13:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.