自动装饰每一个实例方法的类(Automatically decorating every insta

2019-07-28 21:27发布

我想同样的装饰应用到每一个方法在给定的类,比那些开始和结束等__

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

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

  1. 通过一个特殊的装饰标记他们禁用此机制的一些方法
  2. 启用此机制的子类,以及
  3. 启用此机制甚至被添加到这个类在运行时方法

[注:我使用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

Answer 1:

我觉得这是一个元类做得更好,以处理运行时的子类和方法的装饰。 我没有看到一个优雅的方式来与类装饰自动处理的子类。

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>


Answer 2:

你可以这样做(不知道这是最优雅的方式虽然):

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

至于cls.method_name ,你将不得不使用getattr(cls, method_name)



文章来源: Automatically decorating every instance method in a class