3

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

@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]

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

4

2 回答 2

7

尽管 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-ing 来装饰该类(在此处找到)。

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]
于 2013-09-20T08:26:28.467 回答
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/

于 2013-09-20T07:51:01.200 回答