0

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

我知道您可以将 *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)

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

4

2 回答 2

5

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

例如,看一下call5,你用 定义的def call5(x, y, value=None, *args)。该函数有三个参数,其中一个有默认值,然后它有一个*args包罗万象。

然后,您尝试使用三个位置参数(1, 2, 3)和一个关键字参数调用此函数value="Hello"。这不是为此函数设置的有效参数。您尝试以动态方式执行此操作的事实只是转移了注意力。即使您尝试从字面上执行它仍然无法正常工作call5(1, 2, 3 value="Hello"),并且call5(1, 2, value="Hello", 3)是语法错误。这个函数根本不能接受三个位置参数加上一个关键字指定的“值”参数。

您可以定义一个函数,例如可以调用任何函数的“调用者”,但这不会改变位置参数和关键字参数的某些组合可能对该函数无效的事实。这不是您的动态调用结构的产物,而是函数的工作方式:必须使用符合其签名的参数调用它们。如果传递太多参数,函数将失败。如果签名包含可变参数,这会给您一些余地,但它不会改变函数可能仍需要一定数量的参数的事实,如果您传递的参数少于该参数,它将失败。您不能期望传递任何任意元组和参数的字典并让它适用于任何函数。

或者,换一种说法,您可以编写一个通用调用程序来调用任何函数,而调用程序机器不需要知道签名是什么,但是使用调用程序并向其传递参数的都必须知道调用程序的签名是什么最终调用的函数是。

于 2013-06-19T06:45:41.517 回答
0

这在 python2 中不起作用的原因(以及如何在 python3 中正确执行)也可以在这个问题中得到更好的解释:

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

于 2013-06-19T07:41:04.933 回答