你如何透明地将参数传递给python中的函数?

时间:2013-06-19 06:30:47

标签: python

具体来说,当你有一个调用另一个函数的函数时,第二个函数的签名是未知的。

我知道你可以使用* args和** kwargs来做类似的事情,但我似乎无法以一种始终有效的方式使用它。

因此,例如,这样的事情几乎可行:

class Invoker():
  def __init__(self, call):
    self._call = call
  def call(self, *args, **kwargs):
    self._call(*args, **kwargs)

..但如果我为它编写一些测试,我得到这个输出:

1 -> 2  ('Hello')
1 -> 2  ((3, 'Hello'))
1 -> 2  ('Hello'), ({'left': 'right'})
1 -> 2  ((3,)), ({'value': 'Hello'})
call5() got multiple values for keyword argument 'value'
call5() got multiple values for keyword argument 'value'

来自此代码:

# Known argument list: works fine
def call(x, y, value=None):
  print("%d -> %d  (%r)" % (x, y, value))

invoker = Invoker(call)
invoker.call(1, 2, value="Hello")

# Unknown argument list with no kwargs: works fine
def call2(x, y, *args):
  print("%d -> %d  (%r)" % (x, y, args))

invoker = Invoker(call2)
invoker.call(1, 2, 3, "Hello")

# Unknown kwargs with default value: works fine
def call3(x, y, value=None, **kwargs):
  print("%d -> %d  (%r), (%r)" % (x, y, value, kwargs))

invoker = Invoker(call3)
invoker.call(1, 2, value="Hello", left="right")

# Unknown args and kwargs: works fine
def call4(x, y, *args, **kwargs):
  print("%d -> %d  (%r), (%r)" % (x, y, args, kwargs))

invoker = Invoker(call4)
invoker.call(1, 2, 3, value="Hello")

# Default value with unknown args: fails
try:
  def call5(x, y, value=None, *args):
    print("%d -> %d  (%r), (%r)" % (x, y, value, args))

  invoker = Invoker(call5)
  invoker.call(1, 2, 3, value="Hello")
except Exception, e:
  print(e)

# Default value with unknown args and kwargs: fails
try:
  def call6(x, y, value=None, *args, **kwargs):
    print("%d -> %d  (%r), (%r), (%r)" % (x, y, value, args, kwargs))

  invoker = Invoker(call5)
  invoker.call(1, 2, 3, value="Hello", left="right")
except Exception, e:
  print(e)

如果在运行时除了要调用的方法的签名是未知的,那么如何编写一个调用另一个始终有效的方法的方法?

2 个答案:

答案 0 :(得分:5)

问题不在于动态传递参数。问题是你试图使用对该函数无效的参数集来调用函数。

例如,查看call5,您使用def call5(x, y, value=None, *args)定义。该函数有三个参数,其中一个有默认值,然后它有一个*args catchall。

然后尝试使用三个位置参数(1, 2, 3)和关键字参数value="Hello"来调用此函数。这不是为此函数设置的有效参数。你试图以动态的方式做这件事只是一个红鲱鱼;即使您尝试逐字call5(1, 2, 3 value="Hello"),它仍然无效,而call5(1, 2, value="Hello", 3)是语法错误。这个函数根本不能接受三个位置参数加上一个关键字指定的“value”参数。

您可以定义一个类似于“调用者”的函数,它可以调用任何函数,但这并不会改变位置和关键字参数的某些组合可能对该函数无效的事实。这不是动态调用结构的工件,它只是函数的工作原理:必须使用适合其签名的参数调用它们。如果传递的参数太多,则该函数将失败。如果签名包含变量参数,那么会给你一些余地,但它并没有改变这个函数可能仍然需要一定数量的参数的事实,如果你传递的参数少于它,它就会失败。你不能期望传递任何任意元组和参数的dict,并让它适用于任何函数。

或者换句话说,你可以编写一个泛型调用程序来调用任何函数,而调用者机器不需要知道签名是什么,但无论谁使用调用者,并将参数传递给它, 必须知道最终调用函数的签名是什么。

答案 1 :(得分:0)

在这个问题中,更好地解释了为什么这在python2中不起作用(以及如何在python3中正确地完成)的原因:

http://www.stackoverflow.com/questions/4372346/uses-of-combining-kwargs-and-key-word-arguments-in-a-method-signature