gnotnek
gnotnek

Reputation: 319

relay multiple keyword arguments from outer function to inner functions in Python?

I am trying to pass arguments to an outer function that then relays them to one or more inner functions. The closest I've come to achieving this:

def multiply(number, factor=1):
    return number*factor

def add(number, to_add=0):
    return number+to_add

def multiply_add(number, *args):
    return add(multiply(number, *args[0]), *args[1])

multiply_add(5, [3], [200])
Out[]: 215

This solution is impractical in a few ways: 1) The user has to know the order in which to pass the arguments, without using keywords; and 2) These arguments have to be iterables for some reason, otherwise get "TypeError: multiply() argument after * must be an iterable, not int".

My question: how to rewrite multiply_add() so the following works? :

multiply_add(5, factor=3, to_add=200)

P.S. I've seen a working example of this with Seaborn calling additional Matplotlib arguments. For example, making the dot size equal to 25:

sns.jointplot(x='A', y='B', data=df, kind='reg', scatter_kws={'s': 25})

Something in this form would be great too.

Upvotes: 2

Views: 1182

Answers (3)

s3c
s3c

Reputation: 1851

These answers didn't help me much, but I was looking for something else anyway. Since I've figured it out, I thought I might post the answer here, for someone who may get here the same way I did.

In my case I wanted an outer function that would take my inner function as an argument and my inner function would have other arguments, but the inner function along with it's arguments could be different every time.

Granted the solution I found is so easy, probably everyone knows it.

So my outer function times the one time execution of the inner function and print out a pretty output:

def timeFun(function):
  t_start = perf_counter()
  print("---\nResult: ", function, "\nExecution time: ", perf_counter() - t_start, sep="")

Now for the sake of simplicity my inner function is such, that it's pointless to time it:

def sumTogether(*args):
  return sum(args)

Now you simply call the function like this:

timeFun(sumTogether(1, 2, 3, 4))

And the output is:

---     
Result: 10
Execution time: 7.99999999995249e-07

Also:

def time(function):
  t_start = perf_counter()
  print("---\nResult: ", function, "\nExecution time: ", perf_counter() - t_start, sep="")

def sumTogether(*args, just_for_kicks=5):
  return sum(args) + just_for_kicks

time(sumTogether(1, 2, 3, 4, just_for_kicks=10))

Output:

---     
Result: 20
Execution time: 8.999999999981245e-07

Upvotes: 0

gnotnek
gnotnek

Reputation: 319

Ok, I figured out how to do this offline. There are at least two ways.

FIRST WAY: slap **kwargs absolutely everywhere

def multiply(number, factor=1, **kwargs):
    return number*factor

def add(number, to_add=0, **kwargs):
    return number+to_add

def multiply_add(number, **kwargs):
    return add(multiply(number, **kwargs), **kwargs)

multiply_add(5, to_add=200, factor=3)

Note that argument order doesn't matter here.

SECOND WAY: specify different sets of **kwargs and pass them in dict form (my preferred)

def multiply(number, factor=1):
    return number*factor

def add(number, to_add=0):
    return number+to_add

def multiply_add(number, kwargs_multiply, kwargs_add):
    return add(multiply(number, **kwargs_multiply), **kwargs_add)

multiply_add(5, {'factor':3}, {'to_add':200})

Note that argument order DOES matter here: the multiply() arguments need to be specified before the add() arguments.

Upvotes: 1

Hugo
Hugo

Reputation: 138

This is not much beautiful code, but I think it does what you want right ?

    1 def multiply(number, factor=1.0):
    2     return number*factor
    3 
    4 def add(number, to_add=0):
    5     return number+to_add
    6 
    7 def multiply_add(number, **kwargs):
    8     factor = kwargs.get("factor")
    9     to_add = kwargs.get("to_add")
   11     if to_add and factor:
   12         return add(multiply(number, factor), to_add)
   13     elif to_add:
   14         return add(multiply(number), to_add)
   15     elif factor:
   16         return add(multiply(number, factor))

Maybe you prefer:

  1 def multiply(number, factor=1.0):
  2     if factor == None:
  3         factor=1.0
  4     return number*factor
  5 
  6 def add(number, to_add=0):
  7     if to_add == None:
  8        to_add=0
  9     return number+to_add
 10 
 11 def multiply_add(number, **kwargs):
 12     factor = kwargs.get("factor")
 13     to_add = kwargs.get("to_add")
 14     return add(multiply(number, factor), to_add)
 15 print multiply_add(30)
 16 print multiply_add(30, to_add=2)
 17 print multiply_add(30, to_add=25)
 18 print multiply_add(30, factor=8)
 19 print multiply_add(30, factor=2, to_add=6)

Upvotes: 0

Related Questions