Python中的函数链

时间:2016-08-19 11:48:31

标签: python function python-3.x

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

  

创建一个函数add,在连续调用时将数字加在一起。因此add(1)应该返回1add(1)(2)应该返回1+2,...

虽然我熟悉Python的基础知识,但我从来没有遇到过能够连续调用的函数,即可以被称为{{f(x)的函数f(x)(y)(z)...。 1}}。到目前为止,我甚至不确定如何解释这种表示法。

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

如果这种解释是正确的,那么Python将允许我动态创建对我来说非常有趣的函数。我在过去一小时内搜索过网络,但无法找到正确方向的领先优势。既然我不知道如何调用这个编程概念,那么这可能不会太令人惊讶。

您如何称呼这个概念?我在哪里可以阅读更多相关内容?

6 个答案:

答案 0 :(得分:90)

我不知道这是功能链接是否与可调用链接一样多,但是,因为函数 callables我想那里没有伤害。无论哪种方式,我都可以考虑这两种方式:

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子类,返回的值保留__repr__的{​​{1}}和__str__行为。 对于更复杂的操作,您应该适当地定义其他dunders

@Caridorc在评论中指出,int也可以简单地写成:

add

将班级重命名为add = CustomInt 而不是add也同样适用。

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

我能想到的另一种方法涉及一个嵌套函数,它需要一个额外的空参数调用才能返回结果。我使用CustomInt 并选择将属性附加到函数对象以使其在Pythons之间移植:

nonlocal

这会自动返回(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调用才能返回递增的值:

()

答案 1 :(得分:23)

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

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. 相同
  4. 构造新类型并在外部lambda中返回。

答案 2 :(得分:16)

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

下一点是您需要保留所有参数,在这种情况下,您可能希望使用Coroutines或递归函数。但请注意, Coroutines比递归函数更加优化/灵活,特别适用于此类任务。

这是一个使用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

答案 3 :(得分:5)

执行此操作的pythonic方法是使用动态参数:

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

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

答案 4 :(得分:1)

如果您愿意接受其他()来检索结果,则可以使用functools.partial

from functools import partial

def add(*args, result=0):
    return partial(add, result=sum(args)+result) if args else result

例如:

>>> 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

这还允许一次指定多个数字:

>>> 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

如果您希望add(x)(y)(z)轻松返回结果 并且可以进一步调用,则可以使用子类int

答案 5 :(得分:0)

简单地:

class add(int):
   def __call__(self, n):
      return add(self + n)