python передает разные **kwargs нескольким функциям

Из python doc и stackoverflow я понимаю, как использовать **kwargs в моей функции def. Однако мне нужно два набора ** kwargs для двух подфункций. Может ли кто-нибудь показать мне, как правильно разделить **kwargs?

Вот моя цель: построить набор точек и интерполированную гладкую кривую,
и мой наивный пример кода:

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

Спасибо за помощь.


person user3287545    schedule 23.10.2014    source источник
comment
Просто передать scatter и plot как обычные dict? Похоже, что это обязательные аргументы... затем используйте **scatter или **plot в функциях... вы больше ничего с ними не делаете   -  person Jon Clements♦    schedule 23.10.2014


Ответы (3)


Нет такого механизма. Существует предложение, PEP-448, согласно которому Python 3.5 и последующие обобщают аргумент распаковка. Python 3.4 и предыдущие версии его не поддерживают. Лучшее, что вы можете сделать в целом:

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

Затем передайте эти параметры как словари, а не kwargs, в smoothy.

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

Поскольку в этом случае имена переменных, возможно, будут видны вызывающим, вы можете переименовать их во что-то более короткое (например, scatter_options и plot_options).

Обновление: Python 3.5 и 3.6 теперь являются основными и действительно поддерживают расширенный синтаксис распаковки на основе PEP-448.

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

Это, однако, не сильно помогает в этом сценарии kwargs, предназначенном для нескольких адресатов. Даже если бы функция smoothy() взяла унифицированный набор kwargs, вам нужно было бы определить, какие из них предназначены для каких подфункций. Грязный в лучшем случае. Несколько dict параметров, один из которых предназначен для передачи каждой подфункции, принимающей kwarg, по-прежнему лучший подход.

person Jonathan Eunice    schedule 23.10.2014
comment
Как я предложил здесь нет смысла хранить kwargs_for_ в аргументов больше нет... (во всяком случае имхо) - person Jon Clements♦; 23.10.2014
comment
@JonClements Похоже, у нас были условия гонки / одновременное редактирование, но мы пришли к тому же выводу! - person Jonathan Eunice; 23.10.2014
comment
Не беспокойтесь ... совершенно хороший ответ (хотя я предвзят, кашель) ... Я просто не был уверен, так ли это просто или требуется что-то более запутанное. +1 от меня за разъяснение и варианты :) - person Jon Clements♦; 23.10.2014

Другой, более необычный подход

Я понимаю, что немного опаздываю на вечеринку. Однако я наткнулся на аналогичную проблему, когда имел дело с классом, состоящим из нескольких других классов. Я хотел избежать передачи словарей для каждого подкласса (или -функции), и было бы очень анти-сухой скопировать все аргументы классов компонентов и дополнительно рискнуть обновить все из них на более позднем этапе.

Мое решение, конечно, не самое короткое и не очень красивое, но я думаю, что в нем есть определенная элегантность. Я изменил функцию smoothy ниже:

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 = [k for k, v in inspect.signature(plt.scatter).parameters.items()]
    scatter_dict = {k: kwargs.pop(k) for k in dict(kwargs) if k in scatter_args}
    plt.scatter(x,y, **scatter_dict)

    plot_args = [k for k, v in inspect.signature(plt.plot).parameters.items()]
    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

Расшифровка

Для начала составьте список (scatter_args) аргументов, которые принимает первая функция (scatter), используя inspect.signature(). Затем создайте новый словарь (scatter_dict) из kwargs, извлекая только те элементы, которые также есть в нашем списке аргументов. Использование здесь dict(kwargs) гарантирует, что мы зациклимся на копии kwargs, чтобы мы могли изменить оригинал, не сталкиваясь с ошибками. Затем этот новый словарь может быть передан в функцию (разброс), и шаги повторяются для следующей функции.

Ловушка заключается в том, что имена аргументов в kwargs не могут повторяться, так как теперь это один словарь. Таким образом, для предварительно созданных функций, в которых вы не контролируете имена аргументов, у вас могут возникнуть проблемы с этим методом.

Это позволяет мне затем использовать указанный составной класс в качестве родительского (или под)класса (передавая оставшуюся часть kwargs).

person L. IJspeert    schedule 24.10.2019

Используйте класс для помощи

Я пришел к этому вопросу, потому что мне нужно было сделать что-то подобное. Немного подумав, мне показалось, что классовый подход мне поможет. Я надеюсь, что это может распространиться и на некоторых других.

import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import interp1d

class KWAs:
    def __init__(self, algo):
        self.algo = algo
        self.kwargs_dict = {
            'scatter_params':{},
            'plot_params':{}
        } # preloading group keys allows plotting when a kwarg group is absent.

    def add_kwargs_to_dict(self, group_name, **kwargs):
        self.kwargs_dict[group_name] = kwargs

    def list_kwargs(self):
        print('Listing all kwarg groups:')
        for kwargs in self.kwargs_dict:
            print('\tkwarg group {}: {}'.format(kwargs, self.kwargs_dict[kwargs]))
        print()

    def get_kwarg_group(self,group):
        print('kwarg group {}: {}'.format(group, self.kwargs_dict[group]))
        print()

    def smoothy(self, x,y, kind='cubic', order = 3):
        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, **self.kwargs_dict['scatter_params'])
        plt.plot(xn, yn_cor(xn), **self.kwargs_dict['plot_params'])

        plt.show()

kwas = KWAs('LSQ')
N = 20
colors = np.random.rand(N)
area = (20 * np.random.rand(N))**2

kwas.add_kwargs_to_dict('scatter_params', s=area, c=colors, alpha=0.5)
kwas.add_kwargs_to_dict('plot_params', linewidth=2.0, color='r')
kwas.list_kwargs()
kwas.get_kwarg_group('scatter_params')
kwas.get_kwarg_group('plot_params')

x = []; y = []
for i in range(N):
    x.append(float(i)*np.pi/float(N))
    y.append(np.sin(x[-1]))

kwas.smoothy(x, y)

Я не знал, какие параметры вы пытаетесь контролировать с помощью своих kwargs, поэтому я сделал некоторые из примеров matplotlib. Приведенный выше подход работает, и вы можете добавлять неограниченное количество групп kwarg в словарь класса kwargs и добавлять дополнительные методы, которые могут использовать kwarg по желанию.

Вот результат с добавленными параметрами: введите здесь описание изображения

person Thom Ives    schedule 29.11.2018