在codewars.com上,我遇到了以下任务:
创建一个函数
add
,在连续调用时将数字加在一起。因此add(1)
应该返回1
,add(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将允许我动态创建对我来说非常有趣的函数。我在过去一小时内搜索过网络,但无法找到正确方向的领先优势。既然我不知道如何调用这个编程概念,那么这可能不会太令人惊讶。
您如何称呼这个概念?我在哪里可以阅读更多相关内容?
答案 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的答案相同,但所有内容都在一行上发生。
type
可用于构建新类型:type(name, bases, dict) -> a new type
。对于name
,我们提供空字符串,因为在这种情况下不需要名称。对于bases
(元组),我们提供(int,)
,这与继承int
相同。 dict
是类属性,我们附加__call__
lambda。self.__class__(self + v)
与return CustomInt(self + v)
答案 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)