11

Let's say I have a function foo that gets a few parameters

def foo(width, height, depth=0):
    ...

I want to write a wrapper function that gets all of foo's parameters and passes them on, e.g.

def goo(width, height, depth=0):
    ...
    foo(width, height, depth)
    ...

But this is ugly, since I have to repeat the variables and the default values.

What's the idiomatic way to do this in python?

A few options I thought about:

  1. passing to goo a dictionary called foo_params and calling foo(**foo_params) but then is error prone since I don't know if all the arguments are there

  2. writing another wrapper for foo that checks if the params with default values are None and if so doesn't pass them

  3. Putting the default values as constants so I won't repeat them

2
  • 7
    You can just use *args and **kwargs (similar to your option 1, but no need to pass a dict to goo()). If a parameter is missing, the error will bubble up from foo() instead of goo(). Since they have the same function signature, that shouldn't be too confusing. Commented Jul 11, 2017 at 7:10
  • i would recommend using *args and **kwargs as well. its the most general method and widely accepted Commented Jul 11, 2017 at 7:25

2 Answers 2

15

You can use *args and **kwargs syntax to pass an unknown amount of arguments and/or keyword arguments:

>>> def dec(func):
    def inner(*args, **kwargs):
        print('decorated function')
        func(*args, **kwargs)
    return inner

>>> @dec
def func(a, b):
    return a + b

>>> func(1, 2)
decorated function
>>> 

One downside to using *args and **kwargs is that you'll lose the orginal function signature of the decorated function. eg:

>>> help(func)
Help on function inner in module __main__:

inner(*args, **kwargs)

>>> 

The solution is to use functools.wraps(). It basically copies of the data from the decorated function to the wrapper function:

>>> from functools import wraps
>>> 
>>> def dec(func):
    @wraps(func)
    def inner(*args, **kwargs):
        print('decorated function')
        func(*args, **kwargs)
    return inner

>>> @dec
def func(a, b):
    return a + b

>>> func(1, 2)
decorated function
>>> 

A you can see below, if you now do help(func) the original signature for func will be displayed:

>>> help(func)
Help on function func in module __main__:

func(a, b)

>>>
Sign up to request clarification or add additional context in comments.

3 Comments

@ChristianDean does this allow me to use the params in the wrapper function? I mean, without using kwargs.get()
@Dotan Yes. You no longer have to explicitly pass each argument and/or keyword argument to your function. All you're required to do is unpack the arguments tuple args and the keyword argument dictionary kwargs into the call to the decorated function. Python will handle the reset.
I needed to change the second line of inner to return func(*args, **kwargs) in order to make this work.
4

I think you are looking for functools's partial function:

from functools import partial

def foo(a,b):
    return a + b

goo = partial(foo, b = 1)

goo(5)   # returns 6

1 Comment

I don't believe the OP is looking for currying, which is a different concept than wrapping

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.