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

2024-12-23 08:43:00
admin
原创
77
摘要:问题描述:假设我有一些如下代码:def myfunc(anotherfunc, extraArgs): # somehow call `anotherfunc` here, passing it the `extraArgs` pass 我想将另一个现有函数作为anotherfunc参数传递,...

问题描述:

假设我有一些如下代码:

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_ordera_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_argsfunc_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将从 接收ab参数*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 之外,回调函数还可以简单地存储在数据结构中(例如listdict或作为某个类实例的属性),然后稍后使用。关键的见解是函数是 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)
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1120  
  IPD(Integrated Product Development,集成产品开发)流程是一种广泛应用于高科技和制造业的产品开发方法论。它通过跨职能团队的紧密协作,将产品开发周期缩短,同时提高产品质量和市场成功率。在IPD流程中,CDCP(Concept Decision Checkpoint,概念决策检查点)是一个关...
IPD培训课程   75  
  研发IPD(集成产品开发)流程作为一种系统化的产品开发方法,已经在许多行业中得到广泛应用。它不仅能够提升产品开发的效率和质量,还能够通过优化流程和资源分配,显著提高客户满意度。客户满意度是企业长期成功的关键因素之一,而IPD流程通过其独特的结构和机制,能够确保产品从概念到市场交付的每个环节都围绕客户需求展开。本文将深入...
IPD流程   66  
  IPD(Integrated Product Development,集成产品开发)流程是一种以跨职能团队协作为核心的产品开发方法,旨在通过优化资源分配、提高沟通效率以及减少返工,从而缩短项目周期并提升产品质量。随着企业对产品上市速度的要求越来越高,IPD流程的应用价值愈发凸显。通过整合产品开发过程中的各个环节,IPD...
IPD项目管理咨询   76  
  跨部门沟通是企业运营中不可或缺的一环,尤其在复杂的产品开发过程中,不同部门之间的协作效率直接影响项目的成败。集成产品开发(IPD)作为一种系统化的项目管理方法,旨在通过优化流程和增强团队协作来提升产品开发的效率和质量。然而,跨部门沟通的复杂性往往成为IPD实施中的一大挑战。部门之间的目标差异、信息不对称以及沟通渠道不畅...
IPD是什么意思   70  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用