回调中可变数量的参数

Variable number of parameters in callback

我正在尝试在 Python 中实现一个回调系统,这类似于 JavaScript 可以在其回调中使用不同数量的参数。理想情况下,我想在我的回调参数中使用 *args**kwargs 而无需 来实现此目的。

我的目标

我想要的是大致如下所示的内容:

def callback1(val):
    print(val)

def callback2(x, y):
    print(x, y)

def callback3(a, b, c):
    print(a, b, c)

def foo(callback):
    callback(1, 2, 3) # Always has 3 arguments to pass

foo(callback1)  # Fails. Should print "1"
foo(callback2)  # Fails. Should print "1 2"
foo(callback3)  # Ok. Prints "1 2 3"

也许更详细的表达方式是:

# num_params() method isn't real (that I know of), but this is an
# inelegant example of how the callbacks might work if it were
def foo2(callback):
    if num_params(callback) == 1:
        callback(1)
    elif num_params(callback) == 2:
        callback(1, 2)
    elif num_params(callback) == 3:
        callback(1, 2, 3)

我不想要的

我不想在每个回调中使用 *args**kwargs(除非这是不可能的任何其他方式),如下所示:

# This is just SO ugly
def callback1(*args):
    print(args[0])

def callback2(*args):
    print(args[0], args[1])

def callback3(*args):
    print(args[0], args[1], args[2])

JavaScript 等价

这在JavaScript中比较常见。例如,可以为 .forEach() 函数的回调提供 1、2 或 3 个参数:

let myArray = [1, 2, 3, 4]

// Valid
myArray.forEach((element) => {
   // Do stuff with the element only
});

// Valid
myArray.forEach((element, index) => {
   // Do stuff with the element AND the index
});

// Valid
myArray.forEach((element, index, array) => {
   // Do stuff with the element, index and the whole array
});

然而,尽管我在 Google 搜索中尽了最大努力,但我不知道如何在 Python 中实现它(甚至在 JavaScript 中,但那是点;我希望这不会回来咬我)。

我非常想知道这在 Python and/or 中是否可行 and/or 这种编码技术的正确术语是什么。

您可以使用相同数量的参数定义所有回调函数,即:

def callback1(val, b=None, c=None):
    print(val)

def callback2(x, y, c=None):
    print(x, y)

def callback3(a, b, c):
    print(a, b, c)

或者您可以在函数内解压 *args

def callback1(*args):
    val, _, _ = args
    print(val)

def callback2(*args):
    x, y, _ = args
    print(x, y)

def callback3(*args):
    a, b, c = args
    print(a, b, c)

最后,您可以使用 functools.partial 发挥创意。

args 和 kwargs 有什么问题?这是做到这一点的pythonic方式。 Python 不是 JavaScript。如果您不喜欢通过 args[0]args[1] 等索引访问 args,您可以像往常一样定义一些 args,然后休息(未使用的 args)- 在 *args:

def callback1(a, *args):
    print(a)

def callback2(a, b, *args):
    print(a, b)

def callback3(a, b, c):
    print(a, b, c)

你也可以在函数中解压它们:

def callback1(*args):
    a, *rest = args
    print(a)

它使内部更加冗长,但所有回调的定义相同。

命名变量也很常见,您不会使用 _(下划线)代替 argsrest 等:

def callback1(a, *_):
    print(a)

def callback1(*args):
    a, *_ = args
    print(a)