【问题标题】:Automatically decorating every instance method in a class自动装饰类中的每个实例方法
【发布时间】:2012-04-21 11:24:04
【问题描述】:

我想将相同的装饰器应用于给定类中的每个方法,除了那些以__ 开头和结尾的方法。

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

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

  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

【问题讨论】:

  • 您似乎对如何做到这一点有一个可靠的想法,您是否要求人们为您实施它?如果没有,那么如果您有实际问题,为什么不尝试后发布呢?
  • @Lattyware 感谢您指出这一点;我更新了问题以显示我的问题。
  • @anonymous downvoter:请保持礼貌并在投票时评论您不喜欢的内容。我的问题是不是太糟糕了,以至于我什至不值得解释它有什么问题?
  • 呃,不想这么说,但反对票来自我,我给出了我的理由。现在你已经给出了你的实现,问题更具体了,我会删除它。
  • 对不起对不起。完全同意您的反对意见。希望更新后的问题没问题。

标签: python class python-3.x decorator


【解决方案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>

【讨论】:

  • 做得很好。我几乎完成了类似的方法(但在每个 __getattr__ 上调用装饰器而不是预先生成字典),但没有意义:你的更彻底。
  • 注意:要使此代码适用于 Python 3,请删除 __metaclass__ 分配,而改用 class Foo(object, metaclass=decorate_all(printer))。我被困了几分钟,所以希望这对其他人有帮助。
【解决方案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)

【讨论】:

  • 这不仅适用于实例方法,还适用于任何可调用的方法。它也不会自动作用于子类、运行时添加的方法或允许排除。所以它不符合他的大部分要求
  • @agf 没错,您的元类解决方案要好得多。我的解决方案更像是一个丑陋的黑客(如果有人喜欢这些)。
猜你喜欢
  • 2015-03-07
  • 1970-01-01
  • 2019-02-03
  • 2011-06-26
相关资源
最近更新 更多