Python 类方法的最小修饰符
我一开始是这样的:Python 类方法的最小修饰符,python,decorator,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):
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
@states([None])
def open (self, destination):
... open stuff ...
(我不喜欢使用单独的\uuu init\uuu()
和open()
方法,但我调用的东西需要这样做。无论如何,这不是我问题的核心。)
现在,随着方法和状态数量的增加,我想我应该重构成这样:
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
@states([None])
def open (self, destination):
... open stuff ...
其他方法也是如此。基于例如,我为装饰师提出了以下定义:
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
这相当复杂,而且还存在一个问题,即它不会被派生类继承(我的解决方案是简单地使其成为一个全局类)。我的问题是:这是这个问题的最小的、惯用的解决方案,还是我做错了什么?这是我第一次尝试定义我自己的装饰师。我找到的各种参考资料(包括指向
wrapps
)似乎向我不知情的自我暗示,这就是事实。(或者有一个漂亮的库为我封装了这种扭曲吗?快速浏览了一下functools
,但我不能说我真的理解文档,而且无论如何,漂亮的东西似乎>=2.6,而我还需要支持Python 2.5一段时间……)是-此解决方案非常简单,而且可读性很强
您应该这样做——另一种选择是学习“d”编程,并检查可用的Python库以使用面向方面技术——A.O.的用例是
或多或少,这就是:向所有具有共同特征的方法添加锅炉板代码。(在Python中,只需使用适当的模块,无需像Java那样为语言的超集使用替代编译器)您可以使用状态模式:
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)
四人帮的书以不同的方式实现了状态模式。在那里,stateful对象持有对一个状态的引用,所有相关调用都被重定向到此状态。但作者也解释说,这种有状态对象“似乎在运行时改变了它的类”。在Python中,由于其动态性,我们不需要像在动态性较差的语言中那样在运行时模拟类的更改,而是完全这样做。由于只有两个状态,这可能有点过头了,但是当您添加更多的状态和更复杂的转换规则时,它有助于保持方法的简短和简单。每个类代表对象可能处于的特定状态,如果您正确地进行了转换,您可以将其视为一个不变量。我将其修改为:
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
这节省了您键入方括号。我想您可能需要一个描述符,但我还不能将它放在我的脑海中…您可以使用它使其稍微短一点,但这样做很好。顺便说一句,如果您不满意装饰每个重写的方法,考虑使用元模型,它将描述每个方法的允许状态,并向每个状态添加状态检查代码。如果状态集和方法集不变,但方法的实现不变,这是最好的方法。@JochenRitzel:感谢您提供指向装饰器模块的指针。它还帮助我理解
functools
文档。。。我想,我同意,你所拥有的是好的。如果你想去掉括号,你可以def states(*statelist)
,然后用@states('Open','New')
等来装饰。我认为我不想修改我的类层次结构,因为我不希望状态的数量增长太多,我需要有一些类覆盖我目前设置的基类方法;但肯定值得为未来铭记。