一个python装饰器可以创建一个能够智能地操作单个对象和集合对象的函数吗?

时间:2011-05-25 17:08:33

标签: python generics collections decorator

我发现自己编写了很多函数,我希望能够对例如x, y, ...上的参数进行操作。 [(x1, y1, ...), (x2, y2, ...), ...]

是否有一个清晰简单的装饰器,模式或技术来编写这样的函数?

这是一个任意的例子:

def awesome(func):
    # ???

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

mult(3, 4)                      # should return 12
mult((3, 4))                    # should return 12 or possibly (12,)
mult(((3, 4), (2, 3), [3, 3]))  # should return (12, 6, 9)
mult([(3, 4), [4, 5]])          # should return (12, 20)

1 个答案:

答案 0 :(得分:2)

Python 3.4引入了带有single-dispatch generic functions装饰器的@singledispatch,提供了一种在使用不同参数类型调用相同函数时实现不同行为的简单方法。例如,允许您的mult()函数接受迭代:

from functools import singledispatch
from collections.abc import Iterable

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

@mult.register(Iterable)
def _(it):
    return [mult(a, b) for a, b in it]

'基地'函数的版本用@singledispatch修饰,将其转换为通用函数。接下来,函数_()被定义为在可迭代中的每个元素上调用mult(),并使用@mult.register()注册到泛型函数。

虽然上面的例子为了最大限度的通用性而使用collections.abc.Iterable,但它也可以为多种具体类型注册一个函数:

@mult.register(list)
@mult.register(tuple)
def _(it):
    return [mult(a, b) for a, b in it]

使用上述技术,可以编写一个装饰器@takes_iterable,它将任意函数转换为泛型函数,并向其注册另一个函数以适当地处理迭代:

def takes_iterable(func):
    generic = singledispatch(func)
    @generic.register(Iterable)
    def _(it):
        return [func(*args) for args in it]
    return generic

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

使用示例:

>>> mult(17,23)
391
>>> mult([(11, 29), (13, 23), (17, 19)])
[319, 299, 323]

CAVEAT :正如Blckknght在评论中指出的那样,如果您调用一个用@takes_iterable装饰的功能,那么您将获得意想不到的结果,并希望它能够运行在"单一& #34;模式,但传递一个恰好可迭代的参数,就像一个字符串。

这实际上是问题中所要求的行为固有的模棱两可的结果,而不是这里概述的实现,但在考虑是否使用这种技术时,请记住这一点。