8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

在 Python 中如何使用一个函数(回调)作为另一个函数的参数?

Ryan Day 2月前

85 0

假设我有一些类似的代码:def myfunc(anotherfunc, extraArgs): # 以某种方式在这里调用“anotherfunc”,并将“extraArgs”传递给它我想将另一个现有函数作为另一个函数传递...

假设我有一些如下代码:

def myfunc(anotherfunc, extraArgs):
    # somehow call `anotherfunc` here, passing it the `extraArgs`
    pass

我想将另一个现有函数作为 anotherfunc 参数传递,并将参数列表或元组作为传递 extraArgs ,并使用 myfunc 这些参数调用传入的函数。

这可能吗?我该怎么做?

帖子版权声明 1、本帖标题:在 Python 中如何使用一个函数(回调)作为另一个函数的参数?
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由Ryan Day在本站《function》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 概括

    是的,这是可能的。

    在问题中的例子中, anotherfunc 的参数 myfunc 回调 的一个例子 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 - just the function name, not with parentheses after it 。错误的写法 higher_order(my_callback()) 意味着 call my_callback first ,然后将 返回值 ( here, that would be None )传递给 higher_order 。这将导致 TypeError ,因为 None is not callable .

    在函数本身中, 无需执行任何特殊操作 即可接受另一个函数作为参数,也无需通过调用它来使用它。 内部 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 时,这一点尤其重要。许多库专门设计为接受任意参数以转发给回调(例如, the standard library threading ),但其他库则不是(例如,使用 the standard library timeit module 和可调用函数而不是字符串来测试代码)。

    在后一种情况下,参数在传递给 HOF 之前必须先与回调“绑定”。

    See Python Argument Binders to understand how to do this - 这超出了本答案的范围。

    当然,当存储回调以供以后以其他方式使用时(例如, command 在 Tkinter 中创建按钮 a Button in Tkinter ),也适用相同的逻辑。

返回
作者最近主题: