首页 > 解决方案 > 以 dict 作为参数调用函数

问题描述

我目前正在使用argparse. 我Namespacearg_parse. 基本上来自 CLI 的参数应该传递给 2 个函数。现在我正在寻找一种聪明的方法来拆分Namespace这样我可以简单地使用拆包。

下面的例子应该说明我需要什么:

import argparse
ns1 = argparse.Namespace(foo=1)
ns2 = argparse.Namespace(bar=2)
ns3 = argparse.Namespace(foo=1, bar=2)

def f(foo): return foo
def g(bar): return 10 * bar

f(**vars(ns1)) # works
g(**vars(ns2)) # works
f(**vars(ns3)) # does not work, 'bar' is not an argument of f

我可以像这样对过滤器进行硬编码

f_args = ["foo"]
g_args = ["bar"]

f(**{k:v for k, v in vars(ns3).items() if k in f_args}) # works
g(**{k:v for k, v in vars(ns3).items() if k in g_args}) # works

但这感觉很容易出错(如果 的签名f会改变,我必须记住也要改变f_args。)。但是,我可以inspect.getfullargspec用来自动化这部分,例如:

import inspect
f_args = inspect.getfullargspec(f).args

但这一切对我来说都是骇人听闻的,我想知道我是否没有忽略一个简单的模式?


更新

正如@doer_uvc 所指出的,一种方法是使用“catch all”参数来处理我的函数,例如:

def f(foo, **kwargs): return foo

虽然这是一个可行的解决方案,但我很想找到不需要触摸f自身签名的解决方案。

标签: pythonfunctionarguments

解决方案


这没有简写。然而,人们可以很容易地使用它来构建它inspect.signature——这些不是黑客,Python 签名可能会变得非常复杂,并且inspect只是为了这样的目的而存在。

import inspect


def apply(call: 'Callable', kwargs: 'Dict[str, Any]'):
    """Apply all appropriate ``kwargs`` to ``call``"""
    call_kwargs = inspect.signature(call).parameters.keys()
    matching_kwargs = {name: kwargs[name] for name in kwargs.keys() & call_kwargs}
    return call(**matching_kwargs)

这将检查 预期的参数call,并选择 提供的参数kwargs。如果kwargs缺少任何参数call,则抛出缺少参数的标准异常。

>>> def f(foo):
...     return foo
...
>>> apply(f, {'foo': 2, 'bar': 3})
2
>>> apply(f, {'baz': 2, 'bar': 3})
…
TypeError: f() missing 1 required positional argument: 'foo'

推荐阅读