Python中的函数链接
在上,我遇到了以下任务: 创建一个函数Python中的函数链接,python,function,python-3.x,Python,Function,Python 3.x,在上,我遇到了以下任务: 创建一个函数add,在连续调用时将数字相加。所以add(1)应该返回1,add(1)(2)应该返回1+2 虽然我熟悉Python的基础知识,但我从未遇到过能够连续调用的函数,即可以称为f(x)(y)(z)的函数f(x)。到目前为止,我甚至不知道如何解释这个符号 作为一名数学家,我怀疑f(x)(y)是一个函数,它为每个x分配一个函数g{x},然后返回g{x}(y),对于f(x)(y)(z) 如果这种解释是正确的,Python将允许我动态创建对我来说非常有趣的函数。我在网上
add
,在连续调用时将数字相加。所以add(1)
应该返回1
,add(1)(2)
应该返回1+2
虽然我熟悉Python的基础知识,但我从未遇到过能够连续调用的函数,即可以称为f(x)(y)(z)
的函数f(x)
。到目前为止,我甚至不知道如何解释这个符号
作为一名数学家,我怀疑f(x)(y)
是一个函数,它为每个x
分配一个函数g{x}
,然后返回g{x}(y)
,对于f(x)(y)(z)
如果这种解释是正确的,Python将允许我动态创建对我来说非常有趣的函数。我在网上搜索了一个小时,但是没有找到正确方向的线索。然而,由于我不知道这个编程概念是如何被称为的,这可能并不太令人惊讶
你如何称呼这个概念?我在哪里可以读到更多关于它的内容?我不知道这是否是函数链接,也不知道它是可调用链接,但是,既然函数是可调用的,我想这不会造成什么伤害。无论哪种方式,我都可以想到两种方法: 子分类
int
和定义调用
第一种方法是使用自定义的int
子类,该子类定义使用更新的值返回自身的新实例:
class CustomInt(int):
def __call__(self, v):
return CustomInt(self + v)
>>> add(1)(2)()
3
>>> add(1)(2)(3)() # and so on..
6
现在可以定义函数add
来返回CustomInt
实例,该实例作为返回自身更新值的可调用实例,可以连续调用:
>>> def add(v):
... return CustomInt(v)
>>> add(1)
1
>>> add(1)(2)
3
>>> add(1)(2)(3)(44) # and so on..
50
此外,作为int
子类,返回值保留int
的repr\uuu
和\uu str\uu
行为。但是,对于更复杂的操作,您应该适当地定义其他Dunder
正如@Caridorc在评论中指出的,add
也可以简单地写成:
add = CustomInt
将类重命名为add
,而不是CustomInt
,也同样有效
定义一个闭包,需要额外调用以产生值:
我能想到的另一种方法是使用嵌套函数,它需要额外的空参数调用才能返回结果。我正在使用非本地并选择将属性附加到函数对象,以使其在python之间可移植:
def add(v):
def _inner_adder(val=None):
"""
if val is None we return _inner_adder.v
else we increment and return ourselves
"""
if val is None:
return _inner_adder.v
_inner_adder.v += val
return _inner_adder
_inner_adder.v = v # save value
return _inner_adder
这会连续返回自身(\u inner\u adder
),如果提供了val
,则将其递增(\u inner\u adder+=val
),如果没有,则按原样返回值。正如我提到的,它需要额外的()
调用才能返回递增的值:
class CustomInt(int):
def __call__(self, v):
return CustomInt(self + v)
>>> add(1)(2)()
3
>>> add(1)(2)(3)() # and so on..
6
如果要定义要多次调用的函数,首先需要每次返回一个可调用对象(例如函数),否则必须通过定义\uuuu call\uuu
属性来创建自己的对象,才能调用它
下一点是您需要保留所有参数,在本例中,这意味着您可能需要使用或递归函数。但请注意,协程比递归函数优化/灵活得多,特别是对于此类任务
下面是一个使用协程的示例函数,它保留了自身的最新状态。请注意,它不能被多次调用,因为返回值是一个不可调用的整数
,但您可能会考虑将其转换为预期对象;-)
你可以恨我,但这里有一句话:)
编辑:好的,这是怎么回事?代码与@Jim的答案相同,但所有事情都发生在一行上
type
可用于构造新类型:type(name、base、dict)->新类型
。对于name
我们提供了空字符串,因为在这种情况下并不真正需要name。对于base
(tuple),我们提供了一个(int,)
,它与继承int
相同dict
是类属性,我们在其中附加\uuuu调用\uuuu
lambda
self.\uuuu类(self+v)
与return CustomInt(self+v)
新类型在外部lambda中构造并返回
python的方法是使用动态参数:
def add(*args):
return sum(args)
这不是你想要的答案,你可能知道这一点,但我想我还是会给出它,因为如果有人想这样做不是出于好奇,而是为了工作。他们可能应该有“正确的事情做”的答案。简单地说:
class add(int):
def __call__(self, n):
return add(self + n)
如果您愿意接受额外的()
以检索结果,您可以使用:
例如:
>>> add(1)
functools.partial(<function add at 0x7ffbcf3ff430>, result=1)
>>> add(1)(2)
functools.partial(<function add at 0x7ffbcf3ff430>, result=3)
>>> add(1)(2)()
3
如果要将其限制为单个数字,可以执行以下操作:
def add(x=None, *, result=0):
return partial(add, result=x+result) if x is not None else result
如果您希望add(x)(y)(z)
随时返回结果并可进一步调用,那么子分类int
就是一种方法。看起来您正在寻找currying函数。t:嵌套函数是动态创建的,可以访问其父函数的局部变量,并且可以作为(可调用)函数返回对象。@JonathonReinhart这就是我思考问题的方式。但我并没有真正了解如何实现它。顺便说一句:Python肯定会允许您动态创建函数。如果你感兴趣,这里有几个相关的概念可以阅读:| | |@LukasGraf我来看看。非常感谢。在交互式代码中,add=CostumInt
应该也能工作并且更简单。子类化内置代码的问题是,(2*add(1)(2))(3)
失败并出现TypeError
,因为int
不可调用。基本上,CustomInt
在我使用时被转换成普通的int
>>> add(1, 2, 3)(4, 5)(6)()
21
def add(x=None, *, result=0):
return partial(add, result=x+result) if x is not None else result