Python中的函数链接

2024-04-29 15:59:12 发布

您现在位置:Python中文网/ 问答频道 /正文

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

Create a function add that adds numbers together when called in succession. So add(1) should return 1, add(1)(2) should return 1+2, ...

虽然我熟悉Python的基本知识,但我从未遇到过可以连续调用的函数,即可以调用为f(x)(y)(z)...的函数f(x)。到目前为止,我甚至不知道如何解释这个符号。

作为一名数学家,我怀疑f(x)(y)是一个函数,它为每个x分配一个函数g_{x},然后返回g_{x}(y),同样地,对于f(x)(y)(z)

如果这个解释是正确的,Python将允许我动态地创建对我来说非常有趣的函数。我在网上搜索了一个小时,但找不到正确方向的线索。然而,由于我不知道这个编程概念是如何被调用的,这可能并不太令人惊讶。

你怎么称呼这个概念?我在哪里能读到更多关于它的资料?


Tags: 函数comadd概念returnthatcreatefunction
3条回答

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

下一点是需要保留所有参数,在本例中,这意味着您可能需要使用Coroutines或递归函数。但请注意,协同路由比递归函数优化/灵活得多,特别是对于此类任务。

下面是一个使用协程的示例函数,它保留了自身的最新状态。注意,不能多次调用它,因为返回值是一个不可调用的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

你可以恨我,但这里有一句话:)

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

编辑:好的,这是怎么回事?代码与@Jim的答案相同,但所有事情都发生在一行上。

  1. type可用于构造新类型:type(name, bases, dict) -> a new type。对于name我们提供空字符串,因为在这种情况下并不真正需要名称。对于bases(元组),我们提供一个(int,),它与继承int相同。dict是类属性,我们在其中附加__call__lambda。
  2. self.__class__(self + v)return CustomInt(self + v)相同
  3. 新类型是在外部lambda中构造并返回的。

我不知道这是否是函数链接,正如它是可调用的链接一样,但是,由于函数可调用的,我想没有造成任何伤害。不管怎样,我有两种方法可以考虑这样做:

分类int并定义__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子类,返回值保留__repr____str__s的int行为。不过,对于更复杂的操作,应该适当定义其他dunders。

正如@Caridorc在评论中指出的,add也可以简单地写成:

add = CustomInt 

将类重命名为add,而不是CustomInt也同样适用。


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

我唯一能想到的另一种方法是使用嵌套函数,该函数需要额外的空参数调用才能返回结果。我不会使用nonlocal并选择将属性附加到函数对象,使其在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),如果提供了val,则会递增(_inner_adder += val),如果没有,则返回原样的值。就像我提到的,它需要额外的()调用才能返回递增的值:

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

相关问题 更多 >