虽然这个问题非常具体,但我也非常感谢一般建议和其他让我的问题没有实际意义的方法。我正在构建一个集合的AI程序,并且许多函数和类需要处理导致状态之间转换的许多不同的状态和操作,所以我需要一种方法来表示状态和动作。请注意,我不是构建一个简单的状态机,而是构建一些不同的程序(代理),这些程序都采用状态并返回操作作为与环境交互的方式。
我可以使用字符串,但如果特定算法需要将附加信息与状态或操作相关联,那么这很麻烦,并且在长时间运行的程序中反复比较字符串会浪费开销。其他类型的常数也会出现同样的问题。所以我最初的想法是使用嵌套类,如下所示:
class DerivedAgent(Agent):
class StateA(State): pass
class StateB(State): pass
...
def do_something(state):
if state is self.StateA:
...
这很好用,但是如果有很多状态和动作,它可能会占用很多空间来声明它们,并且所有的pass语句都很烦人。我希望能够做点像......
class DerivedAgent(Agent):
states("StateA", "StateB", "StateC", ...)
但我没有办法让states
方法将新创建的类型添加到DerivedAgent
类。我想我可以用inspect
模块做到这一点,但感觉就像是为了一个小便利而走得太远了。使用这样的类型是个坏主意吗?有更优雅的方法吗?代理类之外的代码需要能够访问状态和操作,并且将状态放入模块命名空间不是一个好的选择,因为给定的模块可能包含多个代理。
答案 0 :(得分:2)
您可以使用元类,以便最终获得如下代码:
class DerivedAgent(Agent):
__states__ = ['StateA', 'StateB', ...]
例如:
class AgentMeta(type):
def __new__(meta, classname, bases, classdict):
for clsname in classdict['__states__']:
classdict[clsname] = type(clsname, (State,), {})
return type.__new__(meta, classname, bases, classdict))
然后,只需重写您的Agent
类,使其具有行
#python3.x
class Agent(Base1, Base2, ..., BaseN, metaclass=AgentMeta):
#everything else unchanged
# 2.2 <= python <= 2.7
class Agent(Base1, Base2, ..., BaseN):
__metaclass__ = AgentMeta
#everything else unchanged
如果您不想更改Agent
类,则可以在您创建的每个子类中包含metaclass的approriate声明。
答案 1 :(得分:1)
显式状态机很无聊,你可以在协同程序中使用隐式状态机。但现在可能太多了。
无论如何class StateA(State): pass
与StateA = type("StateA", (State,), {})
完全相同。保存您输入pass
; - )
答案 2 :(得分:0)
如果您需要状态机,请构建一个。状态机是数据驱动的,这意味着状态和转换被编码为数据,而不是类层次结构。
在Python中,词典是 ad hoc 多态分派的机制:
def do_foo(**kwargs):
pass
def do_bar(**kwargs):
pass
dispatch = {
# state : { (transition, next_state) ... }
0: {'a' : (do_foo, 1)},
1: {'a' : (do_bar, 0)},
1: {'b' : (do_bar, None)}, # None -> accept
}
def state_machine(state, input):
"""does the action corresponding to state on input and returns new state"""
current = dispatch[state]
if input in current:
functor, next = current[input]
functor(lexeme=input)
return next
state = 0
for c in 'aaab':
state = state_machine(state, c)
if state is None:
print 'accepted'
break