二元运算符到多运算符装饰器

时间:2012-09-24 22:49:13

标签: python decorator

假设我有一个我作为二元运算符(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

显然,我不能在没有提及的情况下提出有关装饰者的问题,answer

我已经尝试过编写自己的文章,但不能完全让它正常工作,也欢迎任何解释我出错的地方:

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

3 个答案:

答案 0 :(得分: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

答案 1 :(得分: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是什么。

您可能还需要考虑一下您开始解决的列表的哪一端(即您的运营商是否有left or right associativity)。对于像乘法和加法这样的运算符,自(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

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

答案 2 :(得分: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