Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python pass different **kwargs to multiple functions

From python doc and stackoverflow, I understand how to use the **kwargs in my def function. However, I have a case need two sets of **kwargs for two sub functions. Can someone show me how to separate the **kwargs properly?

Here is my goal: to plot set of points and interpolated smooth curve,
and my naive sample code:

def smoothy(x,y, kind='cubic', order = 3, **kwargs_for_scatter, **kwargs_for_plot):
    yn_cor = interp1d(x, y, kind=kind, assume_sorted = False)
    xn = np.linspace(np.min(x), np.max(x), len(x) * order)
    plt.scatter(x,y, **kwargs_for_scatter)
    plt.plot(xn, yn_cor(xn), **kwargs_for_plot);
    return

Thanks for help.

like image 951
user3287545 Avatar asked Oct 23 '14 17:10

user3287545


People also ask

How do you pass multiple Kwargs Python?

Python 3.5+ allows passing multiple sets of keyword arguments ("kwargs") to a function within a single call, using the `"**"` syntax.

How do you pass a dynamic argument in Python?

Passing keyword arguments with **kwargs As you expect it, Python has also its own way of passing variable-length keyword arguments (or named arguments): this is achieved by using the **kwargs symbol. When using **kwargs, all the keywords arguments you pass to the function are packed inside a dictionary.

How do you pass multiple dictionary parameters in Python?

**kwargs: Pass multiple arguments to a function in Python If so, use **kwargs . **kwargs allow you to pass multiple arguments to a function using a dictionary. In the example below, passing **{'a':1, 'b':2} to the function is similar to passing a=1, b=1 to the function.

How do I unpack Kwargs?

Unpacking kwargs and dictionaries You cannot directly send a dictionary as a parameter to a function accepting kwargs. The dictionary must be unpacked so that the function may make use of its elements. This is done by unpacking the dictionary, by placing ** before the dictionary name as you pass it into the function.


2 Answers

There is no such mechanism. There is a proposal, PEP-448, whereby Python 3.5 and following generalize argument unpacking. Python 3.4 and previous don't support it. Best you can do in general:

def smoothy(x,y, kind='cubic', order = 3, kwargs_for_scatter={}, kwargs_for_plot={}):
    yn_cor = interp1d(x, y, kind=kind, assume_sorted = False)
    xn = np.linspace(np.min(x), np.max(x), len(x) * order)
    plt.scatter(x,y, **kwargs_for_scatter)
    plt.plot(xn, yn_cor(xn), **kwargs_for_plot);
    return

Then pass in those options as dictionaries, not kwargs, to smoothy.

smoothy(x, y, 'cubic', 3, {...}, {...})

Because the variable names would then be possibly exposed to callers, you may want to rename them something shorter (perhaps scatter_options and plot_options).

Update: Python 3.5 and 3.6 are now mainstream, and they indeed support an extended unpacking syntax based on PEP-448.

>>> d = {'name': 'joe'}
>>> e = {'age': 20}
>>> { **d, **e }
{'name': 'joe', 'age': 20}

This does not, however, help much in this kwargs-intended-for-multiple-destinations scenario. Even if the smoothy() function took a unified grab-bag of kwargs, you'd need to determine which of them were intended for which subfunctions. Messy at the very best. Multiple dict parameters, one intended to be passed to each kwarg-taking subfunction, still the best approach.

like image 89
Jonathan Eunice Avatar answered Oct 23 '22 11:10

Jonathan Eunice


Another, more different approach

I realize I am a bit late to the party. However, I stumbled across a similar issue when dealing with a class composed of several other classes. I wanted to avoid passing dictionaries for each sub-class (or -function) and it would be very anti-dry to copy all the arguments of the component classes and additionally run the risk of having to update all of them at a later stage.

My solution is certainly not the shortest nor is it very nice, but I think it has a certain elegance to it. I modified the function smoothy below:

import inspect

def smoothy(x,y, kind='cubic', order = 3, **kwargs):
    yn_cor = interp1d(x, y, kind=kind, assume_sorted = False)
    xn = np.linspace(np.min(x), np.max(x), len(x) * order)
    
    scatter_args = list(inspect.signature(plt.scatter).parameters)
    scatter_dict = {k: kwargs.pop(k) for k in dict(kwargs) if k in scatter_args}
    plt.scatter(x,y, **scatter_dict)
    
    plot_args = list(inspect.signature(plt.plot).parameters)
    plot_dict = {k: kwargs.pop(k) for k in dict(kwargs) if k in plot_args}
    plt.plot(xn, yn_cor(xn), **plot_dict);
    return

Explantion

To start off, make a list (scatter_args) of the arguments that the first function (scatter) accepts, using inspect.signature(). Then construct a new dictionary (scatter_dict) from kwargs, only extracting items that are also in our list of arguments. Using dict(kwargs) here ensures that we loop over a copy of kwargs, so that we can alter the original without running into errors. This new dictionary can then be passed to the function (scatter) and the steps are repeated for the next function.

A pitfall is that argument names in kwargs may not be repeated, since it is now a single dict. So for pre-built functions where you do not control argument names you might run into problems with this method.

This does allow me to then use said composed class as a parent (or sub) class (passing on the remainder of kwargs).

like image 19
L. IJspeert Avatar answered Oct 23 '22 09:10

L. IJspeert