自动装饰类中的每个实例方法

时间:2012-04-08 23:01:08

标签: python class python-3.x decorator

我希望将相同的装饰器应用于给定类中的每个方法,而不是以__开头和结尾的方法。

在我看来,使用类装饰器应该是可行的。是否有任何陷阱需要注意?

理想情况下,我也希望能够:

  1. 通过使用特殊装饰器标记它们来禁用某些方法的机制
  2. 为子类启用此机制
  3. 即使对于在运行时添加到此类的方法
  4. ,也启用此机制

    [注意:我正在使用Python 3.2,所以如果这依赖于最近添加的功能,我会很好。]

    这是我的尝试:

    _methods_to_skip = {}
    
    def apply(decorator):
      def apply_decorator(cls):
        for method_name, method in get_all_instance_methods(cls):
          if (cls, method) in _methods_to_skip:
            continue
          if method_name[:2] == `__` and method_name[-2:] == `__`:
            continue
          cls.method_name = decorator(method)
      return apply_decorator
    
    def dont_decorate(method):
      _methods_to_skip.add((get_class_from_method(method), method))
      return method
    

    以下是我遇到的问题:

    • 如何实施get_all_instance_methods功能
    • 不确定我的cls.method_name = decorator(method)行是否正确
    • 如何对运行时添加到类中的任何方法执行相同的操作
    • 如何将其应用于子类
    • 如何实施get_class_from_method

2 个答案:

答案 0 :(得分:15)

我认为使用元类更好地完成,以便处理运行时和子类方法的装饰。我没有看到使用类装饰器自动处理子类的优雅方法。

from types import FunctionType

# check if an object should be decorated
def do_decorate(attr, value):
    return ('__' not in attr and
            isinstance(value, FunctionType) and
            getattr(value, 'decorate', True))

# decorate all instance methods (unless excluded) with the same decorator
def decorate_all(decorator):
    class DecorateAll(type):
        def __new__(cls, name, bases, dct):
            for attr, value in dct.iteritems():
                if do_decorate(attr, value):
                    dct[attr] = decorator(value)
            return super(DecorateAll, cls).__new__(cls, name, bases, dct)
        def __setattr__(self, attr, value):
            if do_decorate(attr, value):
                value = decorator(value)
            super(DecorateAll, self).__setattr__(attr, value)
    return DecorateAll

# decorator to exclude methods
def dont_decorate(f):
    f.decorate = False
    return f

它的一个使用示例(Python 2,但对Python 3进行了简单的修改):

def printer(f):
    print f
    return f

class Foo(object):
    __metaclass__ = decorate_all(printer)
    def bar(self):
        pass
    @dont_decorate
    def baz(self):
        pass
    @classmethod
    def test(self):
        pass
# prints
# <function bar at 0x04EB59B0>

class AnotherName(Foo):
    def blah(self):
        pass
# prints
# <function blah at 0x04EB5930>

Foo.qux = lambda: 1
# prints
# <function <lambda> at 0x04EB57F0>

答案 1 :(得分:0)

你可以这样做(不确定这是否是最优雅的方式):

def get_all_instance_methods(x):
    return filter(callable, map(lambda d: getattr(x, d), dir(x)))

对于cls.method_name,您必须使用getattr(cls, method_name)