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

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

Ryan Day 2月前

80 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)
  • 是的,这是可能的。 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
    
  • @4dc0 我不认为它有直接的文档记录;它只是 Python 数据模型工作方式的自然结果。可以绑定到名称的所有内容都是对象;对象可以传递给函数;函数可以绑定到名称(这是使用 def 时发生的情况);因此,函数可以传递给函数。话虽如此,您可能对 docs.python.org/3.11/howto/ functional.html 感兴趣,它展示了利用这一现实的基本技术,并展示了一些旨在利用它的标准库好东西。

  • 以下是另一种使用方法 *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 .

  • 有关 *args 和 **kwargs 的更多信息,请参见此处 pythontips.com/2013/08/04/args-and-kwargs-in-python-explained

  • Python 中的函数是一等对象。但是,函数定义 应该略有不同 :

    def myfunc(anotherfunc, extraArgs, extraKwArgs):
        return anotherfunc(*extraArgs, **extraKwArgs)
    
  • 当然,这就是为什么 python 实现以下方法时第一个参数是一个函数:

    • map(function, iterable, ...) - 将函数应用于可迭代的每个项目并返回结果列表。
    • filter(function, iterable) - 根据函数返回 true 的可迭代元素构造一个列表。
    • reduce(function, iterable [,initializer]) - 将两个参数的函数从左到右累积地应用于的项 iterable ,从而将可迭代对象减少为单个值。
    • 拉姆达
  • 另请参阅:docs.python.org/3.11/howto/ functional.html。内置函数还包括 sort、min 和 max,它们接受预期为可调用的关键关键字参数。

  • 不过,我并不认为 lambda 属于同一类别。它们不是内置函数(也不是“方法”),而是一种语法;而且 lambda 的第一个参数绝对不需要接受函数。事实上,lambda 根本不需要有参数。

  • 一个函数可以作为另一个函数的参数,一个函数可以返回另一个函数。

    以下是一个例子:

    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
    
  • 装饰器 在 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
    
  • 可以一次调用两个或多个函数,通过将一个函数的调用作为另一个函数的参数:

    def anotherfunc(inputarg1, inputarg2):
        pass
    def myfunc(func = anotherfunc):
        print(func)
    
    myfunc(anotherfunc(inputarg1, inputarg2))
    

    这将导致 myfunc 打印调用的返回值 anotherfunc (即 None )。

  • 是的,这是可能的。像其他函数一样使用该函数: anotherfunc(*extraArgs) .

  • 概括

    是的,这是可能的。

    在问题中的例子中, 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 ),也适用相同的逻辑。

  • def x(a):
        print(a)
        return a
    
    def y(a):
        return a
    
    y(x(1))
    
  • 以下是一个例子:

    def x(a):
        print(a)
        return a
    
    def y(func_to_run, a):
        return func_to_run(a)
    
    y(x, 1)
    
返回
作者最近主题: