def myfunc(anotherfunc, extraArgs):
# somehow call `anotherfunc` here, passing it the `extraArgs`
pass
我想将另一个现有的函数作为anotherfunc
参数传递,并将参数列表或元组作为extraArgs
,并让myfunc
使用这些参数调用传入的函数。
这种做法可行吗?我该如何实现?
def myfunc(anotherfunc, extraArgs):
# somehow call `anotherfunc` here, passing it the `extraArgs`
pass
我想将另一个现有的函数作为anotherfunc
参数传递,并将参数列表或元组作为extraArgs
,并让myfunc
使用这些参数调用传入的函数。
这种做法可行吗?我该如何实现?
是的,这是可能的。myfunc
可以像这样调用传入的函数:
def myfunc(anotherfunc, extraArgs):
anotherfunc(*extraArgs)
这里是一个完整的示例:
>>> def x(a, b):
... print('a:', a, 'b:', b)
...
>>> def y(z, t):
... z(*t)
...
>>> y(x, ('hello', 'manuel'))
a: hello b: manuel
def
时发生的情况);因此,函数可以传递给函数。也就是说,您可能会对https://docs.python.org/3.11/howto/functional.html 感兴趣,其中展示了基本技术,利用了这个现实,并展示了一些标准库好东西 旨在利用它。 - Karl Knechtel*args
(以及可选的**kwargs
)的方法:def a(x, y):
print(x, y)
def b(other, function, *args, **kwargs):
function(*args, **kwargs)
print(other)
b('world', a, 'hello', 'dude')
输出
hello dude
world
function
,*args
和**kwargs
必须以这个顺序出现,并且必须是调用function
的函数(b
)中的最后一个参数。def myfunc(anotherfunc, extraArgs, extraKwArgs):
return anotherfunc(*extraArgs, **extraKwArgs)
当然,这就是为什么Python实现了以下方法,其中第一个参数是函数:
map(function, iterable, ...)
- 将函数应用于可迭代对象的每个项目,并返回结果列表。filter(function, iterable)
- 从可迭代对象中构建一个列表,其中包含函数返回true的元素。reduce(function, iterable [,initializer])
- 将两个参数的函数累积地应用于iterable
的项目,从左到右,以将可迭代对象减少为单个值。一个函数可以作为另一个函数的参数,也可以返回另一个函数。
以下是一个例子:
def out_func(a):
def in_func(b):
print(a + b + b + 3)
return in_func
obj = out_func(1)
print(obj(5)) # outputs 14
是的,这是可能的。像使用其他函数一样使用该函数:anotherfunc(*extraArgs)
。
可以通过将一个函数的调用作为另一个函数的参数来同时调用两个或多个函数:
def anotherfunc(inputarg1, inputarg2):
pass
def myfunc(func = anotherfunc):
print(func)
myfunc(anotherfunc(inputarg1, inputarg2))
这将导致myfunc
打印anotherfunc
调用的返回值(即None
)。
装饰器在Python中非常强大,因为它们允许程序员将一个函数作为参数传递,并且还可以在另一个函数内定义一个函数。
def decorator(func):
def insideFunction():
print("This is inside function before execution")
func()
return insideFunction
def func():
print("I am argument function")
func_obj = decorator(func)
func_obj()
输出:
This is inside function before execution
I am argument function
是的,这是可能的。
在问题的示例中,myfunc
的 anotherfunc
参数是 回调 的一个例子,因此 myfunc
是 高阶函数(以下简称 HOF)的一个例子。
编写 HOF 并为其提供回调的一个简单示例可能如下所示:
def higher_order(a_callback):
print("I will call:", a_callback)
a_callback()
def my_callback():
print("my_callback was called")
higher_order(my_callback)
请注意,示例代码中传递的是my_callback
- 只需使用函数名称,不要在其后加上括号。 如果错误地编写了 higher_order(my_callback())
将意味着首先调用 my_callback
,然后将返回值( 这里将是 None
)传递给 higher_order
。这将导致 TypeError
,因为 None
不可调用。
higher_order
内部,a_callback
是传递进来的任何函数的本地名称(这里是my_callback
);通过编写它们的名称、(
、适当的参数和)
来调用函数;因此,这就是higher_order
需要做的一切,以便使用传递进来的函数。
def my_func(other_func, func_args):
,其中other_func
将是一个回调函数。在函数内部,other_func
只是传递进来的回调函数的名称,并且调用它的方式与调用任何其他函数相同。我们需要一个名称(或任何其他求值为可调用对象的表达式),然后是(
,然后是调用的任何适当参数,然后是)
。例如,假设func_args
应该是可调用对象的变量参数序列,我们可以通过解包调用中的参数进行此调用。因此:def my_func(other_func, func_args):
other_func(*func_args)
dict
(或其他映射),高阶函数可以使用**
解包将其传递给回调函数。因此:def my_func(other_func, func_args, func_kwargs):
other_func(*func_args, **func_kwargs)
当然,我们决不局限于这种基本逻辑。my_func
像任何其他函数一样工作。在调用other_func
之前或之后,它可以执行任何其他任意的工作;它可以return
或以其他方式使用other_func
的结果;它可以多次调用other_func
(或有条件地根本不调用它);它可以使用自己的逻辑来确定要传递给回调的参数(甚至完全在本地确定它们而不具有像func_args
或func_kwargs
的参数),等等。
为了使用这个高阶函数,调用代码需要两个东西:一个适当的可调用对象作为回调(即其签名必须与高阶函数如何调用它兼容),以及适当的调用高阶函数的代码。
继续上面的例子,假设我们有一个类似于以下的回调函数:
def my_callback(a, b, /, **c):
print(f'a = {a}; b = {b}; c = {c}')
由于之前的my_func
将在调用时使用*
和**
,应用于来自调用者的输入,因此对于my_callback
的签名没有特定的限制。但是,由于my_func
将从*func_args
接收a
和b
参数,并且因为my_func
标记这些参数为位置参数, 所以传递给my_func
的func_args
需要是长度为2的序列。(func_kwargs
本来就应该是字典;它将被解包以供回调调用,然后回调将再次打包它。
因此:
def my_func(other_func, func_args, func_kwargs):
other_func(*func_args, **func_kwargs)
def my_callback(a, b, /, **c):
print(f'a = {a}; b = {b}; c = {c}')
# call `my_func`, giving it the callback `my_callback`
# as well as appropriate arguments to call the callback:
my_func(my_callback, [1, 2], {'example': 3})
由于高阶函数只是简单地调用回调函数,它实际上并不关心回调函数是否为函数。利用鸭子类型的优势,我们也可以传递例如类等其他类型的参数。这对于使用回调函数进行“类型检查”的高阶函数特别有用(例如标准库argparse
就是这样做的):
def ask_user_for_value(type_checker):
while True:
try:
return type_checker(input('give me some input: '))
except Exception as e:
print(f'invalid input ({e}); try again')
# using an existing type:
ask_user_for_value(int)
# using a custom function for verification:
def capital_letter(text):
if len(text) != 1:
raise ValueError('not a single character')
if not (text.isupper() and text.isalpha()):
raise ValueError('not an uppercase letter')
return text
ask_user_for_value(capital_letter)
# using an enum: (in 3.11 this could use StrEnum)
from enum import Enum
class Example(Enum):
ONE = 'one'
TWO = 'two'
ask_user_for_value(Example)
# using a bound method of an instance:
class Exact: # only allow the specified string
def __init__(self, value):
self._value = value
def check(self, value):
if value != self._value:
raise ValueError(f'must be {self._value!r}')
return value
ask_user_for_value(Exact('password').check)
除了定义高阶函数之外,回调函数还可以简单地存储在数据结构中,例如list
、dict
或作为某个类实例的属性,然后稍后再使用。关键的洞见是,在Python中函数是对象,因此它们可以以这种方式存储,并且任何求值为函数对象的表达式都可以用于调用该函数。
示例:
def my_callback():
print('my_callback was called')
# in a list
funcs = [my_callback]
for i in funcs:
i()
# in a dict
funcs = {'my_callback': my_callback}
funcs['my_callback']()
# in a class instance
class Example:
def __init__(self, callback):
self._callback = callback
def use_callback(self):
self._callback()
instance = Example()
instance.use_callback()
有时,我们想要使用现有的回调函数,但它需要除了高阶函数提供的参数之外的其他参数。当使用来自第三方代码的高阶函数时,这尤其重要。许多库专门设计为接受任意参数以转发到回调函数(例如标准库threading
),但其他库则不然(例如,使用可调用对象而非字符串进行测试代码的标准库timeit
模块)。
在后一种情况下,在将回调传递给高阶函数之前,必须将参数“绑定”到回调函数。
请参见Python参数绑定器以了解如何执行此操作 - 这超出了本答案的范围。
当回调被存储以便以其他方式稍后使用时(例如,作为在创建Tkinter中的按钮时提供的command
),相同的逻辑当然适用。def x(a):
print(a)
return a
def y(a):
return a
y(x(1))