Ruggero Turra
Ruggero Turra

Reputation: 17680

How to propagate default values

Support I have defined some functions, e.g. (very simplified example)

def f1(a, b, arg='something'):
   ...
def f2(c, arg='else'):
   ...

Then I call use them as:

def f(a, b, c):
   return f1(a, b) + f2(c)

Since this is very commonly used, but I want to have f1 and f2 defined separately so that it is possible to call just one.

I want to have the possibility when calling f to call f1 and f2 with different default arguments, so I can do:

def f(a, b, c, arg1='something', arg2='else'):
   return f1(a, b, arg1) + f2(c, arg2)

This is not very maitanable (I have many more functions than 2 and many more arguments), since when I update one function I need to update f. One options would be to use None as default values and then change them inside the body of the functions, e.g.

def f1(a, b, arg=None):
   arg = arg or 'something'
   ...

but this hides the real value of the default argument from the signature. In addition this doesn't solve all my problems since if I add one new argument to f1 I have to change the signature of f and also its implementation...

Upvotes: 2

Views: 184

Answers (4)

Tomasz Kurgan
Tomasz Kurgan

Reputation: 221

Use None (or sentinel, if None is actually a meaningful value) as a default value and then set the "real" default value inside the function.

Example

def f1(a, b, arg=None):
    arg = arg or 'something'
    ...
def f2(c, arg=None):
    arg = arg or 'else'
    ...

def f(a, b, c, arg1=None, arg2=None):
    return f1(a, b, arg1) + f2(c, arg2)

This way, if a parametr is not specified, the default value will propagate up the calling tree.

Upvotes: 0

WloHu
WloHu

Reputation: 1527

def f1(a, b, arg='something', **kwargs):
   pass
def f2(c, arg='else', **kwargs):
   pass
def f(a, b, c, arg1='something', arg2='else'):
   kwargs = locals()
   return f1(**kwargs) + f2(**kwargs)

If you want something from both worlds namely dynamic delegation and static definitions this is the most simple solution but it's not ideal:

  • you have to add **kwargs to functions so they don't fail when too many arguments are passed;
  • you will get logical errors easily on argument renames, e.g. rename c in f2 to b.

If you have many fn functions the return line may end up like this:

return sum(f(**kwargs) for f in (f1, f2, ..., fn))

Upvotes: 0

koneru nikhil
koneru nikhil

Reputation: 339

Try using this function!

def f(a, b, c, *args):
   if len(args)==2:
      return(f1(a, b, args[0]) + f2(c, args[1]))
   else:
      return(f1(a, b, None) + f2(c, None))

Upvotes: 0

Charles Landau
Charles Landau

Reputation: 4265

It sounds like you are getting to the point where object oriented programming can help you. You could rewrite your functions as a class:

class FFuncs:
        def __init__(self, a, b, c, arg1="foo", arg2="bar"):
            self.a = a
            self.b = b
            self.c = c
            self.arg1 = arg1
            self.arg2 = arg2
        def f1(self):
          return str(self.a + self.b) + self.arg1
        def f2(self):
          return str(self.c) + self.arg2
        def f(self):
          return self.f1() + self.f2()

FFuncs(2, 3, 5, arg1=" Foos and ", arg2=" Spam").f()
# Returns "5 Foos and 5 Spam"

Now you always have access to the default values of your kwargs before each execution. And as mentioned in the comments, if you need an especially flexible call signature for any given method of your class, you can still abstract away with *args, **kwargs.

Upvotes: 1

Related Questions