Python中的函数链接
在codewars.com我遇到了以下任务:
创build一个函数
add
,在连续调用时将数字相加。 所以add(1)
应该返回1
,add(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的答案是一样的,但是一切都发生在一行上。
-
type
可以用来构造新的types:type(name, bases, dict) -> a new type
。 对于name
我们提供空string,因为名字在这种情况下并不是真的需要。 对于bases
(元组),我们提供了一个(int,)
,它与inheritanceint
相同。dict
是类属性,我们附上了__call__
lambda。 -
self.__class__(self + v)
与return CustomInt(self + v)
- 新的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)
这不是你正在寻找的答案,你可能知道这一点,但我想我会放弃它,因为如果有人想知道这样做不是出于好奇,而是工作。 他们应该可能有“正确的事情”的答案。