Как я могу переопределить значение **kwargs в определении функции, чтобы он вместо этого распаковывал dict в стандартные аргументы функции def?

Можно ли в Python распаковать слова аргументов ключевых слов в определении функции? Насколько я вижу, это невозможно, потому что есть два независимых определения синтаксиса двойной звезды. Распаковка возможна только тогда, когда вызывается функция, и никогда, когда она определяется. Это правда? Если так, есть ли способ обойти это, чтобы выполнить нечто подобное тому, что я хочу сделать? Другими словами, могу ли я переопределить это поведение?

Есть два варианта использования двойных звезд **, Один, ** может использоваться для передачи dict (и распаковки) функции. Два, **kwargs может использоваться при определении функции для указания неопределенного количества аргументов ключевого слова. Насколько я могу судить, это два совершенно независимых (хотя и логически непротиворечивых) определения **,
Подробное описание здесь:
Что ** (двойная звезда) и * (звезда) делают для параметров Python?

Простые примеры каждого.

def print_args(**kwargs):
    print kwargs   

print_args(one='this', two='that')
# {'two': 'that', 'one': 'this'}

def print_kw(one=None, two=None):
    print one; print two 

print_kw(**{'one':'this', 'two':'that'})
# this
# that

Что я хотел бы сделать, это:

packed_keywords = {'apple':'red', 'peach':'fuzzy'}
def print_unpacked_kw(**packed_keywords):
    print apple; print peach

print_unpacked()
# NameError: global name 'apple' is not defined 
# I'd like this to print out "red fuzzy"

Для сравнения приведу пример аналогичного кода без распаковки. Эта версия работает, но без использования dict для ключевых слов args, как я желаю.

def print_typed_kw(apple='red', peach='fuzzy'):     
    print apple; print peach

print_typed_kw()
# red
# fuzzy

РЕДАКТИРОВАТЬ: Почему я хочу сделать это?

КОНТЕКСТ:
Это объяснение очень специфично для scikit-learn. Если вы не знакомы с этой библиотекой, лучше всего игнорировать оставшуюся часть этого контекстного раздела. Этот вопрос возникает в контексте написания класса преобразователя, который будет идти внутри конвейера. В частности, я создаю преобразователь, который будет возвращать прогноз от регрессора. Моя идея состоит в том, чтобы использовать этот прогноз как одну функцию в Союзе объектов, которая войдет в другой нижестоящий классификатор.

Одним из преимуществ конвейера является установка параметров в Grid Search для оптимизации гиперпараметров. По моему опыту, таким образом можно получить доступ к параметрам пользовательской функции, только если параметры определены как аргументы в __init__ конструктор класса оценки. Вот мой класс:

class RandForestTransformer(BaseEstimator, TransformerMixin):
    """
    Takes a random forest (or could be any classifier) and uses 
    predict as output for transform, which can then be used as
    a feature in another FeatureUnion and classifier. 
    """
    def __init__(self, 
                 n_estimators=10, criterion='mse', 
                 max_depth=None, min_samples_split=2, 
                 min_samples_leaf=1, min_weight_fraction_leaf=0.0, 
                 max_features='auto', max_leaf_nodes=None, 
                 bootstrap=True, oob_score=False, n_jobs=1, 
                 random_state=None, verbose=0, warm_start=False): 
        self.rf = RandomForestRegressor()  
    def fit(self, X, y):
        self.rf = self.rf.fit(X, y)
        return self
    def transform(self, X, y=None):
        return self.rf.predict(X)

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

РЕДАКТИРОВАТЬ 2:
Что касается моей конкретной проблемы, я хотел бы поблагодарить @ja за то, что он предложил взглянуть на код scEkit-learn BaseEstimator.
https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/base.py

Определение класса для BaseEstimator явно гласит, что параметры должны быть заданы в __init__ где это самоанализ для использования трубопровода.

class BaseEstimator(object):
    """Base class for all estimators in scikit-learn
    Notes
    -----
    All estimators should specify all the parameters that can be set
    at the class level in their ``__init__`` as explicit keyword
    arguments (no ``*args`` or ``**kwargs``).
    """   

1 ответ

Решение

Вы пытаетесь применить всеохватывающее **kwargs аргумент в ваш locals Пространство имен. Вы не можете сделать это из-за ограничений оптимизации в Python; Локальные переменные на самом деле являются массивом C, в котором интерпретатор ищет переменные по индексу (байт-код, созданный для функций, использует индексы, а не строки для ссылки на локальные объекты).

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

В более широком контексте ваш RandForestTransformer.__init__ Метод не использует ни одного из ключевых аргументов, поэтому нет смысла определять все эти имена. Возможно, что scikit-learn использует самоанализ для этого метода, чтобы увидеть, какие переменные использует конвейер, но если это так, то замените список аргументов ключевого слова на **kwargs не будет работать, так как это заберет один источник для самоанализа.

Другие вопросы по тегам