2

我从这样的事情开始:

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 ...

对于其他方法也是如此。基于例如这个经典的SO答案,我为装饰器提出了以下定义:

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

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

4

3 回答 3

2

是的——这个解决方案非常接近你所能得到的简单——而且它是可读的。

你应该去做 - 另一种方法是学习“ Aspect Oriente d”编程,并检查可用的 Python 库以使用方面方向 - AO 的用例或多或少是这样的:将样板代码添加到所有具有共同特征。(在 Python 中,这只是使用适当模块的问题,不需要像 Java 那样为语言的超集使用替代编译器)

于 2012-09-25T19:35:14.017 回答
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

这样可以节省您键入方括号的时间。

于 2012-09-25T20:58:23.903 回答
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 中,由于它的动态性,我们不需要在运行时模拟类的更改,就像我们必须在动态性较低的语言中那样,而是完全这样做。只有两个状态可能是多余的,但是当您添加更多状态并具有更复杂的转换规则时,它有助于保持方法的简短和简单。每个类都代表对象可以处于的特定状态,如果您的转换正确,您可以将其视为不变量。

于 2012-09-25T20:50:52.667 回答