得到kwargs Inside Function

时间:2010-01-18 17:52:24

标签: python introspection kwargs

如果我有这样的python函数:

def some_func(arg1, arg2, arg3=1, arg4=2):

有没有办法确定哪个参数是通过关键字从函数内部传递的?

修改

对于那些问我为什么需要这个的人,我没有真正的理由,它出现在对话中,好奇心让我变得更好。

6 个答案:

答案 0 :(得分:16)

不,在使用此签名的Python代码中无法执行此操作 - 如果您需要此信息,则需要更改函数的签名。

如果你看一下Python C API,你会发现参数传递给普通Python函数的实际方式总是作为一个元组加一个字典 - 即直接反映签名的方式*args, **kwargs。然后将该元组和字典解析为特定的位置args和在签名中命名的那些,即使它们是按名称传递的,*a**kw(如果存在)只会占用“溢出”从那个解析,如果有的话 - 只有在这一点上你的Python代码才能获得控制权,然后你要求的信息(如何是通过的各种args)不再存在。< / p>

因此,要获取您请求的信息,请将签名更改为*a, **kw并进行自己的解析/验证 - 这是“从鸡蛋到煎蛋”,即一定量的工作,但肯定可行,而你正在寻找的将是“从煎蛋回到鸡蛋”......根本不可行; - )。

答案 1 :(得分:12)

这是我通过装饰者的解决方案:

def showargs(function):
    def inner(*args, **kwargs):
        return function((args, kwargs), *args, **kwargs)
    return inner

@showargs
def some_func(info, arg1, arg2, arg3=1, arg4=2):
    print arg1,arg2,arg3,arg4
    return info

In [226]: some_func(1,2,3, arg4=4)
1 2 3 4
Out[226]: ((1, 2, 3), {'arg4': 4})

可能有一种方法可以进一步清理它,但这似乎对我的干扰最小,并且不需要更改调用代码。

编辑:要实际测试是否按关键字传递了特定的args,请在some_func中执行以下操作:

args, kwargs = info
if 'arg4' in kwargs:
    print "arg4 passed as keyword argument"

免责声明:您应该考虑一下您是否真的关心如何传递参数。整个方法可能是不必要的。

答案 2 :(得分:3)

你几乎不得不重新定义你的功能:

def some_func(*args, **kwargs):

自己做编组。没有办法区分pass-by-position,pass-by-keyword和default。

答案 3 :(得分:1)

您想知道arg31是因为它是从外部传递还是因为它是默认值?据我所知,没有办法做到这一点。我怀疑,主要原因是没有必要这样的知识。通常做的是以下内容:

>>> def func(a, b=None):
    if b is None:
# here we know that function was called as:
# func('spam') or func('spam', None) or func('spam', b=None) or func(a='spam', b=None)

        b = 42

答案 4 :(得分:1)

就这样做:

def some_func ( arg1, arg2, arg3=None, arg4=None ):
    if arg3 is None:
        arg3 = 1 # default value
    if arg4 is None:
        arg4 = 2 # default value

    # do something

通过这种方式,您可以看到设置了某些内容,,您还可以使用更复杂的默认结构(如列表),而不会遇到如下问题:

>>> def test( arg=[] ):
        arg.append( 1 )
        print( arg )
>>> test()
[1]
>>> test()
[1, 1]

答案 5 :(得分:1)

  

是否有办法确定函数中关键字传递了哪些参数?

在尝试评估关键字参数的默认值时,可以使用以下选项:

代码

选项1-locals()

def f(a, b=1, c="1"):
    print(locals())


f(0)
# {'c': '1', 'b': 1, 'a': 0}

选项2-部分类型提示 *

def g(a, b:int=1, c:float="1"):
    pass


keys = g.__annotations__
values = g.__defaults__

dict(zip(keys, values))
# {'b': 1, 'c': '1'}

选项3-完整类型提示 *

def h(a:float, b:int=1, c:str="1") -> int:
    return 0


keys = reversed(list(filter(lambda x: x != "return", h.__annotations__)))
values = reversed(h.__defaults__)

{k: v for k, v in zip(keys, values) if k != "return"}
# {'c': '1', 'b': 1}

注意:这些选项都不是特别Pythonic的,但它们显示出了潜力。


详细信息

  1. locals()取决于函数调用。结果应该是默认值,但是它们会随着传入呼叫的​​值而改变,例如f(0)f(0 2, 3)
  2. “部分”类型提示意味着仅注释了关键字参数。添加任何其他注释将不适用于这种幼稚的方法。
  3. “完整”或完整类型提示可能包含其他参数。我们向后迭代,以避免使用可选的位置参数压缩值。由于"return"注释是可选的,因此我们在迭代过程中对其进行过滤。

*这些选项取决于类型提示和键插入顺序保留(Python 3.6+)。它们仅提供默认值,不会随函数调用值而变化。类型提示现在在Python中是可选的,因此在生产代码中应谨慎使用。


建议

我只会使用后一种方法来调试或快速检查功能的签名。实际上,给定仅关键字参数,就可以使用inspect.getargspec()来捕获kwonlydefaults字典。

def f(a, *, b=1, c="1"):
    pass


spec = inspect.getfullargspec(f)
spec
# FullArgSpec(args=['a'], varargs=None, varkw=None, defaults=None, 
#             kwonlyargs=['b', 'c'], kwonlydefaults={'b': 1, 'c': '1'}, 
#             annotations={})

spec.kwonlydefaults
# {'b': 1, 'c': '1'}

否则,将某些技术与args的{​​{1}}和defaults属性结合:

FullArgSpec