Search code examples
pythonpython-3.xfunctionoptional-arguments

Is it possible to pass the same optional arguments to multiple functions?


I want to ask if there is a way to prevent unnecessary duplicate of code when passing the same arguments into a function's optional arguments.

Hopefully the following example provides a good idea of what I am trying to do:

def f(arg1):
    def g(optional_1=0, optional_2=0, optional_3=0):
        return arg1+optional_1+optional_2+optional_3
    return g

b, c = 2, 3
f1 = f(1)
f2 = f(2)
calc_f1 = f1(optional_2=b, optional_3=c)
calc_f2 = f2(optional_2=b, optional_3=c)

As you can see, f1 and f2 only differ in the arg1 passed into f and afterwards I call them with the same variables for the same optional arguments. It is fine when the code is short, but when I have over 10 optional arguments, it becomes unnecessarily long and redundant. Is it possible to do something like

optional_variable_pair = #some way to combine them
calc_f1 = f1(optional_variable_pair)
calc_f2 = f2(optional_variable_pair)

so I get a more succinct and easy to read code?


Solution

  • To answer the question you asked, the answer is yes. You can do almost exactly what you want using keyword argument unpacking.

    def f(arg1):
        def g(optional_1=0, optional_2=0, optional_3=0):
            return arg1+optional_1+optional_2+optional_3
        return g
    
    optional_variable_pair = {
        'optional_2': 2,
        'optional_3': 3
    }
    
    f1 = f(1)
    f2 = f(2)
    calc_f1 = f1(**optional_variable_pair)
    calc_f2 = f2(**optional_variable_pair)
    

    If I'm reading your intent correctly, though, the essence of your question is wanting to pass new first arguments with the same successive arguments to a function. Depending on your use case, the wrapper function g may be unnecessary.

    def f(arg1, *, optional_1=0, optional_2=0, optional_3=0):
        return optional_1 + optional_2+optional_3
    
    optional_variable_pair = {
        'optional_2': 2,
        'optional_3': 3
    }
    
    calc_f1 = f(1, **optional_variable_pair)
    calc_f2 = f(2, **optional_variable_pair)
    

    Obviously, if the first argument continues incrementing by one, a for loop is in order. Obviously, if you are never using the optional_1 parameter, you do not need to include it. But, moreover, if you find yourself using numbered arguments, there is a good chance you really should be working with tuple unpacking instead of keyword unpacking:

    def f(*args):
        return sum(args)
    
    optional_variable_pair = (2, 3)
    
    for i in range(1, 3):
        calc = f(i, *optional_variable_pair)
        # ...do something with calc...
    

    You may also be interested in researching functools.partial, as well, which can take the place of your wrapper function g, and allow this:

    import functools
    
    def f(*args):
        return sum(args)
    
    f1 = functools.partial(f, 1)
    f2 = functools.partial(f, 2)
    
    calc_f1 = f1(2, 3) # = 1 + 2 + 3 = 6
    calc_f2 = f2(2, 3) # = 2 + 2 + 3 = 7