Дайте функции по умолчанию аргументы из словаря в Python
Представьте, что у меня есть диктат:
d = {'a': 3, 'b':4}
Я хочу создать функцию f, которая делает то же самое, что и эта функция:
def f(x, a=d['a'], b=d['b']):
print(x, a, b)
(не обязательно печатать, но делать что-то с переменной и вызывать напрямую от их имени).
Но я бы хотел создать эту функцию прямо из текста, то есть, я хотел бы иметь что-то похожее на
def f(x, **d=d):
print(x, a, b)
и это ведет себя как ранее определенная функция. Идея состоит в том, что у меня есть большой словарь, который содержит значения по умолчанию для аргументов моей функции, и я не хотел бы делать
def f(a= d['a'], b = d['b'] ...)
Я не знаю, возможно ли это вообще в Python. Любое понимание приветствуется!
Изменение: идея заключается в том, чтобы иметь возможность позвонить f(5, a=3)
.
Edit2: Вопрос не в том, чтобы передать аргументы, сохраненные в dict, в функцию, а в том, чтобы определить функцию, имена аргументов и значения по умолчанию которой хранятся в dict.
Ответы
Ответ 1
Python спроектирован так, что локальные переменные любой функции могут быть определены однозначно, если взглянуть на исходный код функции. Итак, предложенный вами синтаксис
def f(x, **d=d):
print(x, a, b)
не является стартером, потому что нет ничего, что указывает, являются ли a
и b
локальными по отношению к f
или нет; это зависит от значения словаря во время выполнения, значение которого может меняться при каждом запуске.
Если вы можете смириться с явным перечислением имен всех ваших параметров, вы можете автоматически устанавливать их значения по умолчанию во время выполнения; это уже хорошо освещено в других ответах. В любом случае, перечисление имен параметров, вероятно, является хорошей документацией.
Если вы действительно хотите синтезировать весь список параметров во время выполнения из содержимого d
, вам придется построить строковое представление определения функции и передать его в exec
. Так работает, например, collections.namedtuple
.
Переменные в областях модуля и класса ищутся динамически, так что это технически допустимо:
def f(x, **kwargs):
class C:
vars().update(kwargs) # don't do this, please
print(x, a, b)
Но, пожалуйста, не делайте этого, кроме как в записи IOPCC.
Ответ 2
Вы не можете достичь этого при определении функции, потому что Python статически определяет область действия функции. Хотя можно написать декоратор для добавления в аргументы по умолчанию ключевых слов.
from functools import wraps
def kwargs_decorator(dict_kwargs):
def wrapper(f):
@wraps(f)
def inner_wrapper(*args, **kwargs):
new_kwargs = {**dict_kwargs, **kwargs}
return f(*args, **new_kwargs)
return inner_wrapper
return wrapper
Usage
Usage
@kwargs_decorator({'bar': 1})
def foo(**kwargs):
print(kwargs['bar'])
foo() # prints 1
Или, если вы знаете имена переменных, но не их значения по умолчанию...
@kwargs_decorator({'bar': 1})
def foo(bar):
print(bar)
foo() # prints 1
Caveat
Вышеприведенное можно использовать, например, для динамического создания нескольких функций с разными аргументами по умолчанию. Хотя, если параметры, которые вы хотите передать, одинаковы для каждой функции, было бы проще и более идиоматично просто передать dict
параметров.
Ответ 3
попробуйте это:
# Store the default values in a dictionary
>>> defaults = {
... 'a': 1,
... 'b': 2,
... }
>>> def f(x, **kwa):
# Each time the function is called, merge the default values and the provided arguments
# For python >= 3.5:
args = {**defaults, **kwa}
# For python < 3.5:
# Each time the function is called, copy the default values
args = defaults.copy()
# Merge the provided arguments into the copied default values
args.update(kwa)
... print(args)
...
>>> f(1, f=2)
{'a': 1, 'b': 2, 'f': 2}
>>> f(1, f=2, b=8)
{'a': 1, 'b': 8, 'f': 2}
>>> f(5, a=3)
{'a': 3, 'b': 2}
Спасибо Ольвину Роту за указание, как правильно объединить словари в python> = 3.5
Ответ 4
Этот вопрос очень интересен, и, похоже, разные люди по-своему догадываются, чего на самом деле хочет этот вопрос.
У меня тоже есть свои. Вот мой код, который можно выразить:
# python3 only
from collections import defaultdict
# only set once when function definition is executed
def kwdefault_decorator(default_dict):
def wrapper(f):
f.__kwdefaults__ = {}
f_code = f.__code__
po_arg_count = f_code.co_argcount
keys = f_code.co_varnames[po_arg_count : po_arg_count + f_code.co_kwonlyargcount]
for k in keys:
f.__kwdefaults__[k] = default_dict[k]
return f
return wrapper
default_dict = defaultdict(lambda: "default_value")
default_dict["a"] = "a"
default_dict["m"] = "m"
@kwdefault_decorator(default_dict)
def foo(x, *, a, b):
foo_local = "foo"
print(x, a, b, foo_local)
@kwdefault_decorator(default_dict)
def bar(x, *, m, n):
bar_local = "bar"
print(x, m, n, bar_local)
foo(1)
bar(1)
# only kw_arg permitted
foo(1, a=100, b=100)
bar(1, m=100, n=100)
выход:
1 a default_value
1 m default_value
1 100 100
1 100 100
Ответ 5
Как насчет ** трюка с Кваргсом?
def function(arg0, **kwargs):
print("arg is", arg0, "a is", kwargs["a"], "b is", kwargs["b"])
d = {"a":1, "b":2}
function(0., **d)
результат:
arg is 0.0 a is 1 b is 2
Ответ 6
Вы можете распаковать значения dict:
from collections import OrderedDict
def f(x, a, b):
print(x, a, b)
d = OrderedDict({'a': 3, 'b':4})
f(10, *d.values())
UPD.
Да, можно реализовать эту безумную идею модификации локальной области видимости, создав декоратор, который будет возвращать класс с переопределением __call__()
и сохранять ваши значения по умолчанию в области видимости класса, НО ЭТО МАССИВНОЕ ПЕРЕКРЫТИЕ.
Ваша проблема в том, что вы пытаетесь скрыть проблемы вашей архитектуры за этими хитростями. Если вы сохраняете свои значения по умолчанию в dict, то доступ к ним осуществляется по ключу. Если вы хотите использовать ключевые слова - определите класс.
Постскриптум Я до сих пор не понимаю, почему этот вопрос собирает столько голосов.
Ответ 7
Конечно...
надеюсь, это поможет
def funcc(x, **kwargs):
locals().update(kwargs)
print(x, a, b, c, d)
kwargs = {'a' : 1, 'b' : 2, 'c':1, 'd': 1}
x = 1
funcc(x, **kwargs)