Как я могу переопределить значение **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
не будет работать, так как это заберет один источник для самоанализа.