在 Python 中如何使用一个函数(回调)作为另一个函数的参数?
- 2024-12-23 08:43:00
- admin 原创
- 78
问题描述:
假设我有一些如下代码:
def myfunc(anotherfunc, extraArgs):
# somehow call `anotherfunc` here, passing it the `extraArgs`
pass
我想将另一个现有函数作为anotherfunc
参数传递,并将参数列表或元组作为传递extraArgs
,并myfunc
使用这些参数调用传入的函数。
这可能吗?我该怎么做?
解决方案 1:
是的,这是可能的。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
解决方案 2:
以下是另一种使用方法*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
函数 () 的最后一个参数。b
`function`
解决方案 3:
Python 中的函数是一等对象。但是,函数定义应该略有不同:
def myfunc(anotherfunc, extraArgs, extraKwArgs):
return anotherfunc(*extraArgs, **extraKwArgs)
解决方案 4:
当然,这就是为什么 python 实现以下方法时第一个参数是一个函数:
map(function, iterable, ...)
- 将函数应用于可迭代的每个项目并返回结果列表。filter(function, iterable)
- 从函数返回 true 的可迭代元素中构造一个列表。reduce(function, iterable [,initializer])
`iterable`- 将两个参数的函数从左到右累积地应用于的项,从而将可迭代对象减少为单个值。lambda 表达式
解决方案 5:
一个函数可以作为另一个函数的参数,一个函数可以返回另一个函数。
以下是一个例子:
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
解决方案 6:
装饰器在 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
解决方案 7:
可以一次调用两个或多个函数,通过将一个函数的调用作为另一个函数的参数:
def anotherfunc(inputarg1, inputarg2):
pass
def myfunc(func = anotherfunc):
print(func)
myfunc(anotherfunc(inputarg1, inputarg2))
这将导致myfunc
打印调用的返回值anotherfunc
(即None
)。
解决方案 8:
是的,这是可能的。像其他函数一样使用该函数:anotherfunc(*extraArgs)
。
解决方案 9:
概括
是的,这是可能的。
在问题中的例子中,anotherfunc
的参数是回调myfunc
的一个例子,因此是高阶函数(以下称为HOF)的一个例子。myfunc
等式两边的一个简单示例(编写 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
使用传入函数所需要做的一切。
撰写 HOF
假设我们尝试定义def my_func(other_func, func_args):
,其中other_func
为回调函数。在函数中,other_func
只是传入的回调函数的名称,调用它的方式与调用任何其他函数 相同。我们需要一个名称(或任何其他计算结果为应调用的可调用(
函数的表达式),然后,然后是调用的任何适当参数,然后)
。例如,假设func_args
应该是可调用函数的可变参数序列,我们可以通过解包调用 中的参数来进行此调用。因此:
def my_func(other_func, func_args):
other_func(*func_args)
类似地,需要关键字参数的回调可以从将传递 a dict
(或其他映射)的另一个参数接收它们,HOF 可以通过**
解包将其传递给回调。因此:
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
这样的参数),等等。
将回调函数传递给 HOF
为了使用这个 HOF,调用代码需要两样东西:一个适当的可调用函数来作为回调传递(即,它的签名必须与 HOF 调用它的方式兼容),以及调用 HOF 本身的适当代码。
继续上面的例子,假设我们有一个回调函数
def my_callback(a, b, /, **c):
print(f'a = {a}; b = {b}; c = {c}')
由于前者my_func
将使用*
和**
进行调用,应用于来自调用者的输入,因此对 的签名没有特别的限制my_callback
。但是,由于my_func
将从 接收a
和b
参数*func_args
,并且由于my_func
将这些参数标记为仅位置,因此func_args
传递给 的my_func
将需要是长度为 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})
其他类型的回调
由于 HOF 只是调用回调,因此它实际上并不关心回调是否是函数。利用duck typing,我们还可以传递例如类。这对于使用回调进行“类型检查”的 HOF 特别有用(例如标准库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)
使用回调的其他方法
除了定义 HOF 之外,回调函数还可以简单地存储在数据结构中(例如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()
特殊情况:提供名人堂不会提供的论据
有时,我们想使用现有的回调函数,但它需要 HOF 提供的参数以外的其他参数。这在使用来自第三方代码的 HOF 时尤其重要。许多库专门设计为接受任意参数以转发给回调(例如,标准库threading
),但其他库则不是(例如,使用带有可调用函数的标准库timeit
模块而不是字符串来测试代码)。
在后一种情况下,参数必须先“绑定”到回调,然后才能将其传递给 HOF。
请参阅Python Argument Binders以了解如何执行此操作- 这超出了本答案的范围。
当然,当存储回调以供以后以其他方式使用时(例如,在Tkinter 中创建command
时提供),也适用相同的逻辑。Button
解决方案 10:
def x(a):
print(a)
return a
def y(a):
return a
y(x(1))
解决方案 11:
以下是一个例子:
def x(a):
print(a)
return a
def y(func_to_run, a):
return func_to_run(a)
y(x, 1)
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)