我开始时有这样的事情:
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一段时间......)
答案 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中,由于它的动态性,我们不需要在运行时模拟类的更改,就像我们必须使用动态性较差的语言一样,而是完全相同。只有两个状态它可能有点过分,但随着您添加更多状态并具有更复杂的转换规则,它有助于保持方法简短。每个类都表示对象可以处于某种状态,如果你的转换正确,你可以将其视为不变量。