Python中的函数链接

在codewars.com我遇到了以下任务:

创build一个函数add ,在连续调用时将数字相加。 所以add(1)应该返回1add(1)(2)应该返回1+2 ,…

虽然我熟悉Python的基础知识,但是我从来没有遇到过能够被调用的函数,也就是函数f(x)可以被称为f(x)(y)(z)... 到目前为止,我甚至不知道如何解释这个符号。

作为一个math家,我猜想f(x)(y)是一个函数,它赋给每个x函数g_{x} ,然后返回g_{x}(y) ,同样对于f(x)(y)(z)

如果这个解释是正确的,那么Python将允许我dynamic地创build对我来说很有意思的函数。 我在过去的一个小时里search了网页,但没能find正确的方向。 因为我不知道这个编程概念是如何被调用的,所以这可能不会太令人吃惊。

你怎么称呼这个概念,我在哪里可以阅读更多关于它的内容?

我不知道这是否是链接的函数链,但是,因为函数是可以调用的,所以我认为没有什么坏处。 无论哪种方式,我可以想到这样做的两种方式:

子类int和定义__call__

第一种方法是定义一个定义__call__ int子类,它使用更新后的值返回一个新的自己的实例:

 class CustomInt(int): def __call__(self, v): return CustomInt(self + v) 

函数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____str__行为。 对于更复杂的操作,你应该适当地定义其他的dunders

正如@Caridorc在评论中指出的那样, add也可以写成:

 add = CustomInt 

重命名类add而不是CustomInt作品也类似。


定义一个闭包,需要额外的调用来产生价值:

我能想到的唯一的另一种方式涉及一个嵌套的函数,需要一个额外的空参数调用才能返回结果。 我没有使用nonlocal并select将属性附加到函数对象,使其在Pythons之间可移植:

 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 

这个_inner_adder返回( _inner_adder ),如果提供了一个val ,就会增加它( _inner_adder += val ),如果不是,则返回原来的值。 就像我之前提到的那样,它需要一个额外的()调用才能返回递增的值:

 >>> add(1)(2)() 3 >>> add(1)(2)(3)() # and so on.. 6 

你可以恨我,但这里是一个单一的:)

 add = lambda v: type("", (int,), {"__call__": lambda self, v: self.__class__(self + v)})(v) 

编辑:好的,这是如何工作的? 代码和@Jim的答案是一样的,但是一切都发生在一行上。

  1. type可以用来构造新的types: type(name, bases, dict) -> a new type 。 对于name我们提供空string,因为名字在这种情况下并不是真的需要。 对于bases (元组),我们提供了一个(int,) ,它与inheritanceint相同。 dict是类属性,我们附上了__call__ lambda。
  2. self.__class__(self + v)return CustomInt(self + v)
  3. 新的types被构造并在外部lambda中返回。

如果你想定义一个被多次调用的函数,首先你需要每次返回一个可调用的对象(例如一个函数),否则你必须通过定义一个__call__属性来创build自己的对象,以便调用它。

接下来的一点是,您需要保留所有的参数,在这种情况下,您可能需要使用协程或recursion函数。 但是请注意, 协程比recursionfunction更加优化/灵活 ,特别适用于这些任务。

下面是使用协程的示例函数,它保留了它自己的最新状态。 请注意,它不能被多次调用,因为返回值是一个不可调用的integer ,但是你可能想把它变成你期望的对象;-)。

 def add(): current = yield while True: value = yield current current = value + current it = add() next(it) print(it.send(10)) print(it.send(2)) print(it.send(4)) 10 12 16 

pythonic方式做到这一点将是使用dynamic参数:

 def add(*args): return sum(args) 

这不是你正在寻找的答案,你可能知道这一点,但我想我会放弃它,因为如果有人想知道这样做不是出于好奇,而是工作。 他们应该可能有“正确的事情”的答案。