Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pythonic way to write wrapper functions

Tags:

python

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

like image 392
Dotan Avatar asked Jul 11 '17 07:07

Dotan


2 Answers

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)

>>>
like image 81
Christian Dean Avatar answered Oct 01 '22 05:10

Christian Dean


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
like image 39
FLab Avatar answered Oct 01 '22 06:10

FLab