将默认装饰器应用于类的所有方法,并有选择地排除它们

时间:2013-09-20 07:34:11

标签: python python-2.7 python-decorators

我有一个包含大量方法的类,其中大部分都需要先运行一个方法来填充类中的列表。但是我也想使用延迟加载,这样我就可以在没有初始加载的情况下创建实例,直到调用需要大量加载的方法。因此,我想创建一个类,其中假定所有方法都需要运行给定方法,除非选择性地排除该方法。这是我的伪代码:

@all_methods_run_load
class myClass
    things = []
    params = []

    @dont_run_load
    def __init__(self, params):
      self.params = params

    @dont_run_load
    def load(self):
      self.things = do_a_ton_stuff()

    def get_things():
      return self.things

    def get_some_things():
      return apply_a_filter(self.things)

    def get_first_thing():
      return self.things[0]

    def get_last_thing():
      return self.things[-1]

希望这是有道理的。我对装饰者本身很新,并且仍然记住它们,所以我担心答案可能会让我大吃一惊,不过这个想法在我脑海中浮现,我不禁进一步调查:)

2 个答案:

答案 0 :(得分:5)

虽然Aidan Kane可能是正确的,你不应该这样做,但是陈述的问题的解决方案可以这样说:

这是可以装饰方法的函数。

import functools

def run_load(function):
    """Make a function run the class' load method before running."""
    @functools.wraps(function)
    def inner(self, *args, **kwargs):
        self.load()
        return function(self, *args, **kwargs)
    return inner

功能是可变的,所以你可以只标记它们。

def without_load(function):
    """Tag a method so that it shouldn't be decorated to call self.load."""
    function.without_load = True
    return function

您可以通过浏览其成员并setattr对其进行装饰(found here)。

import inspect

def decorate_all_with(decorator, predicate=None):
    """Apply a decorator to all methods that satisfy a predicate, if given."""

    if predicate is None:
        predicate = lambda _: True

    def decorate_all(cls):
        for name, method in inspect.getmembers(cls, inspect.isfunction):
            if predicate(method):
                setattr(cls, name, decorator(method))

        return cls

    return decorate_all

就是这样!

def should_be_loaded(function):
    try:
        return not bool(function.without_load)
    except AttributeError:
        return True

@decorate_all_with(run_load, should_be_loaded)
class MyClass:
        things = []
        params = []

        @without_load
        def __init__(self, params):
            self.params = params

        @without_load
        def load(self):
            self.things = do_a_ton_stuff()

        def get_things(self):
            return self.things

        def get_some_things(self):
            return apply_a_filter(self.things)

        def get_first_thing(self):
            return self.things[0]

        def get_last_thing(self):
            return self.things[-1]

答案 1 :(得分:4)

我对你想要实现的目标感到困惑。如果你想延迟加载,为什么你没有需要在第一次只评估的属性上调用数据的函数?

class myClass
    _things = []
    params = []

    def __init__(self, params):
      self.params = params

    @property
    def things(self):
      if not self._things:
          self._things = do_a_ton_stuff()
      return self._things

    def get_things():
      return self.things

    def get_some_things():
      return apply_a_filter(self.things)

    def get_first_thing():
      return self.things[0]

    def get_last_thing():
      return self.things[-1]

有些人为这种模式制作一个@cachedproperty装饰器 - 所以他们不必自己做self._things位。 http://code.activestate.com/recipes/576563-cached-property/