Python类或函数的装饰器

5

我想征求您的意见,以下是否合理或者是否有更好的方法。基本上我想要一个装饰器,它可以应用于实现__call__的函数或类。

您可以使用常规装饰器并显式地装饰__call__,但是这样装饰器就被嵌套在类定义中,不太明显。也许我错过了更简单的解决方案。

import types
from functools import wraps

class dec:
    """ Decorates either a class that implements __call__ 
        or a function directly.
    """
    def __init__(self, foo):
        self._foo = foo

    def __call__(self, target):
        wraps_class = isinstance(target, types.ClassType)
        if wraps_class:
            fun = target.__call__
        else:
            fun = target

        @wraps(fun)
        def bar(*args, **kwds):
            val = args[1] if wraps_class else args[0]
            print self._foo, val
            return fun(*args, **kwds)
        if wraps_class:
            target.__call__ = bar
            return target
        else:
            return bar

@dec('A')
class a:
    # you could decorate here, but it seems a bit hidden
    def __call__(self, val):
        print "passed to a:", val

@dec('B')
def b(val):
    print "passed to b:", val

a()(11)
b(22)
3个回答

4

个人建议将其分成两个装饰器: 一个始终包装函数:

def func_dec(foo, is_method=False):
    def wrapper(fun):
        @wraps(fun)
        def bar(*args, **kwds):
            val = args[1] if is_method else args[0]
            print foo, val
            return fun(*args, **kwds)
        return bar
    return wrapper

另一种方法是检测是否应该修改__call__方法或者简单地包装一个函数:

def dec(foo):
    def wrapper(obj):
        if inspect.isclass(obj):
            obj.__call__ = func_dec(foo, is_method=True)(obj.__call__)
            return obj
        else:
            return func_dec(foo)(obj)
    return wrapper

请注意,inspect.isclass在旧式类和新式类中都能正确运行。

3

我不太喜欢你的方法。如果调用一个实例,就会使用__call__()方法。调用类本身会触发__init__()方法,因此我认为这并不是真正类似的。

你的装饰器对新式类(直接或间接从object继承)无效。如果你想要的话,简单地装饰__call__()方法会更好。或者编写一个工厂函数来创建和装饰类的实例——这与装饰函数完全类似,因为该实例可以直接调用,而不必操纵self参数。


谢谢。 (如果有人感兴趣,DzinX指出使其与新式类一起工作的修复方法是使用inspect.isclass进行检查。) - David Schein

1

这是一个相当巧妙的想法。对我来说看起来很好,尽管直接装饰__call__可能更符合Python风格,因为“显式优于隐式”。有一个装饰器可以做两件事情,这会有一点概念上的开销。

(我在想,将任何函数装饰器转换为双重函数/类装饰器的装饰器是否会更糟糕或更好...)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接