2

假设我有一个已编写为二进制运算符 (binop) 的函数,如何将其扩展为采用任意数量参数的多运算符 (multiop)?库中是否有这样的装饰器(例如在functools中)?

例如(我想要一个装饰器来提供这种行为):

@binop_to_multiop
def mult(a,b):
    return a*b

mult(2,3,4) # 2*3*4 = 24
mult(7) # 7
mult(2,3) # 6

显然,我不能不提这个答案就问一个关于装饰器的问题。

.

我试过自己写,但不能完全正常工作,也欢迎对我哪里出错的任何解释:

def binop_to_multiop(f):
    @functools.wraps(f)
    def wrapper(*args, **kwds):
        if len(args) == 1: return args[0] # fails
        return f(args[0],(f(*args[1:], **kwds)), **kwds) #recursion attempt fails
    return wrapper

给出一个 TypeError: mult() takes exactly 2 arguments (N given) (对于各种N!=2)。

4

3 回答 3

4

reduce()想到:

from functools import wraps

def binop_to_multiop(binop):
    @wraps(binop)
    def multiop(x, *xs):
        return reduce(binop, xs, x)
    return multiop

# ...

@binop_to_multiop
def mult(a, b):
    return a * b

print mult(2, 3, 4)
print mult(7)
print mult(2, 3)

结果:

$ python multiop.py
24
7
6
于 2012-09-24T22:57:50.580 回答
3

您自己编写代码的尝试非常接近工作。您只需要更改递归步骤以进行递归,wrapper而不是将除一个参数之外的所有参数传递给f

def binop_to_multiop(f):
    @functools.wraps(f)
    def wrapper(*args, **kwds):
        if len(args) == 1: return args[0]
        return f(args[0], wrapper(*args[1:], **kwds), **kwds)
    return wrapper

我对基本案例没有任何问题,所以我不确定你的评论#fails是关于什么的。

您可能还需要考虑从列表的哪一端开始求解(即,您的运算符具有左关联性还是右关联性)。对于像乘法和加法这样的运算符,因为没有关系(a+b)+c = a+(b+c),但对于其他运算符,您可能会得到奇怪的结果。例如,减法可能不会像您预期的那样工作:

@binop_to_multiop
def sub(a, b):
    return a - b

使用上面定义的装饰器,sub(a, b, c)将给出不同的结果a-b-c(它会a-(b-c)代替(a-b)-c)。如果您希望它们以相同的方式运行,您可以重新定义装饰器以保持关联(就像大多数计算机语言中的大多数数学运算符一样),如下所示:

def left_associative_binop_to_multiop(f):
    @functools.wraps(f)
    def wrapper(*args, **kwds):
        if len(args) == 1: return args[0]
        return f(wrapper(*args[:-1], **kwds), args[-1], **kwds)
    return wrapper

一种更复杂的方法是将关联性作为装饰器的参数,但如果您不希望该参数是必需的,这将变得棘手。

于 2012-09-24T23:02:15.853 回答
3
def binop_to_multiop(f):
    def wrapper(*args):
        return reduce(f, args) if args else None
    return wrapper

@binop_to_multiop
def mult(a, b):
    return a*b

print mult(2,3,4)
print mult(7)
print mult(2,3)
print mult(4,5,6,7)

给 24 7 6 840

于 2012-09-24T23:03:12.267 回答