类方法的最小装饰器

时间:2012-09-25 19:08:33

标签: python decorator

我开始时有这样的事情:

class Client (object):
    def __init__ (self):
        self.state = None
    def open (self, destination):
        if self.state not in [None]: raise error ...
        ... open stuff ...
        self.state = 'Open'
    def handle (self):
        if self.state not in ['Open'] raise error ...
        ... handle stuff ...
    def close (self):
        if self.state not in ['Open'] raise error ...
        ... close stuff ...
        self.state = None

(我对单独的__init__()open()方法感到不满意,但我所称的东西需要这样的方式,我很害怕。无论如何,这不是我的问题的核心。)

现在,随着方法和状态的数量不断增加,我认为我应该重构这样的事情:

    @states([None])
    def open (self, destination):
        ... open stuff ...

和其他方法类似。基于例如this classic SO answer我想出了装饰器的以下定义:

from functools import wraps

def states (statelist):
    def decorator (f):
        @wraps(f)   # In order to preserve docstrings, etc.
        def wrapped (self, *args, **kwargs):
            if self.state not in statelist: raise error ...
            return f(self, *args, **kwargs)
        return wrapped
    return decorator

这是相当复杂的,并且还有一个问题,它不会被派生类继承(我的解决方案只是使它成为一个全局)。我的问题是:这是解决这个问题的最小的惯用解决方案,还是我做错了什么?这是我第一次尝试定义自己的装饰器。我发现的各种参考文献(包括this one指向我wraps)似乎向我不知情的自己暗示,这实际上是它必须如何。 (或者是否有一个漂亮的图书馆为我封装了这种扭曲?快速浏览一下functools但我不能说我真的理解文档,无论如何,漂亮的东西似乎是> = 2.6虽然我需要支持Python 2.5一段时间......)

3 个答案:

答案 0 :(得分:2)

是的 - 这个解决方案非常接近你可以得到的 - 而且它是可读的。

你应该选择它 - 另一种方法是学习“Aspect Oriente d”编程,并检查可用的Python库是否使用方面方向--A.O的用例。是 或多或少:将锅炉板代码添加到具有共同特征的所有方法中。 (在Python中,只需要使用一个合适的模块,不需要使用替代编译器来实现Java的超集语言)

答案 1 :(得分:2)

我会将其修改为:

def states(*states):
    def decorator (f):
        @wraps(f)   # In order to preserve docstrings, etc.
        def wrapped(self, *args, **kwargs):
            if self.state not in states: raise error ...
            return f(self, *args, **kwargs)
        return wrapped
    return decorator

这可以节省您输入方括号的费用。

答案 2 :(得分:1)

您可以使用状态模式:

class AlreadyClosedError(Exception): pass
class AlreadyOpenError(Exception): pass

class Client(object):

    def __init__(self):
        self._change_state(Closed)

    def _change_state(self, state):
        self.__class__ = state


class Closed(Client):

    def open(self, destination):
        # opening stuff, and if successful:
        self._change_state(Open)

    def handle(self):
        raise AlreadyClosedError()

    def close(self):
        raise AlreadyClosedError()


class Open(Client):

    def open(self, destination):
        raise AlreadyOpenError()

    def handle(self):
        """ handling stuff """

    def close(self):
        # closing stuff, and if successful:
        self._change_state(Closed)

“四人帮”一书以不同的方式实现了状态模式。在那里,有状态对象持有对状态的引用,并且所有相关的调用被重定向到该状态。但作者还解释说,这种状态对象“似乎在运行时改变了它的类”。在Python中,由于它的动态性,我们不需要在运行时模拟类的更改,就像我们必须使用动态性较差的语言一样,而是完全相同。只有两个状态它可能有点过分,但随着您添加更多状态并具有更复杂的转换规则,它有助于保持方法简短。每个类都表示对象可以处于某种状态,如果你的转换正确,你可以将其视为不变量。