Распаковка аргументов ключевого слова, но только те, которые соответствуют функции - программирование
Подтвердить что ты не робот

Распаковка аргументов ключевого слова, но только те, которые соответствуют функции

Скажем, у меня есть функция:

def foo(a = None, b=None, c=None):
  return "a:%s, b:%s, c:%s" % (a,b,c)

У меня есть словарь с некоторым (или ничем) из приведенных выше аргументов, но также с ключами, которые не названы аргументами в функции, например:

d = {'a':1, 'x':4, 'b':2, 'y':5}

Если я вызову следующее, я получу ошибку, потому что "x" и "y" не являются аргументами в аргументе функции foo.

foo(**d) # error

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

Пожалуйста, исправьте меня, если моя аргумент/параметр терминологии выключен.

4b9b3361

Ответ 1

@Ashwini Chaudhary имеет очень питоновский способ решения вашей проблемы. Однако для этого требуется изменить подпись вашей функции foo.

Если вы не хотите изменять свою подпись функции, вы можете использовать интроспекцию, чтобы узнать, какие аргументы ожидает ваша функция:

arg_count = foo.func_code.co_argcount
args = foo.func_code.co_varnames[:arg_count]

args_dict = {}
for k, v in d.iteritems():
    if k in args:
        args_dict[k] = v

foo(**args_dict)

Ответ 2

def foo(a = None, b=None, c=None,**extras):
    return "a:%s, b:%s, c:%s" % (a, b, c)

здесь **extras будет собирать все дополнительные аргументы имени/ключевого слова.

Ответ 3

Интересный вопрос. Я думаю, что большинство людей в реальной жизни используют подход @Ashwini Chaudhary.

Я согласен с @Rodrigue в том, что есть моменты, когда вы не можете изменить сигнатуру вызова функции (возможно, кто-то еще модуль).

Когда это произойдет, Используйте декоратор функций

from inspect import getargspec
from funtools import wraps

def allow_kwargs(foo):
    argspec = getargspec(foo)
    # if the original allows kwargs then do nothing
    if  argspec.keywords:
        return foo
    @wraps(foo)
    def newfoo(*args, **kwargs):
        #print "newfoo called with args=%r kwargs=%r"%(args,kwargs)
        some_args = dict((k,kwargs[k]) for k in argspec.args if k in kwargs) 
        return foo(*args, **some_args)
    return newfoo


# with your function:
@allow_kwargs
def foo(a = None, b=None, c=None):
  return "a:%s, b:%s, c:%s " % (a,b,c)

# with someone_elses function:
from some_place import foo
foo = allow_kwargs(foo)

@wraps из functools держит строки __name__ и __doc__ в такте. Вы также можете:

  • Посмотрите FunctionMaker из модуля декораторов, но это должно быть более многоразовым подходом.
  • изменить newfoo, чтобы не пропускать лишние ключи без ключевых слов.