重命名已应用于计算机的状态和转换

时间:2017-04-05 11:09:04

标签: python transitions

我目前正在使用tyarkoni/transitions为Python动态创建状态机。但是我已经到了一个地步,我想重命名一个状态和它的转换。我想知道是否有办法在不删除和以不同名称重新添加状态的情况下执行此操作。

更具体地说,我正在做的是在通过具有一组子系统的未知系统进行BF搜索时创建状态和转换以及状态的实际名称/ ID系统有时可以被发现"在搜索的后期比找到状态并添加到状态机(然后以临时名称/ id保存)。我想在程序的其他部分重命名它们以便于处理等。

有一种简单的方法吗?

我认为您可以重命名状态的示例

<div class="a">
<p>lorem ipsum dolor site amet...</p>
</div>

<div class="b">

</div>

上述结果在Python中执行

from transitions import Machine
s = ['A','B','C'] # States
t = [['ab','A','B'],['bc','B','C'],['ca','C','A']] # Transitions
m = Machine(states=s,transitions=t,initial='A')
m.state # Current state ('A')
m.get_state('A') # Gets state 'A' (<State('A')@140486642630440>)
m.get_state('A').name # Gets state name 'A' ('A')
m.get_state('A').name = 'D' # Renames state name 'A' to 'D'
m.get_state('A') # Gets state 'A' (<State('D')@140486642630440>)
m.get_state('A').name # Gets state name 'A' ('D')
# The following generates an error, since the state isn't renamed in the machine
m.get_state('D') # (ValueError: State 'D' is not a registered state.)

这不起作用,因为我只重命名名称字符串,而不是实际状态。 我想要的结果是对状态>>> from transitions import Machine >>> s = ['A','B','C'] # States >>> t = [['ab','A','B'],['bc','B','C'],['ca','C','A']] # Transitions >>> m = Machine(states=s,transitions=t,initial='A') >>> m.state # Current state ('A') 'A' >>> m.get_state('A') # Gets state 'A' (<State('A')@140486642630440>) <State('A')@140329230661784> >>> m.get_state('A').name # Gets state name 'A' ('A') 'A' >>> m.get_state('A').name = 'D' # Renames state name 'A' to 'D' >>> m.get_state('A') # Gets state 'A' (<State('D')@140486642630440>) <State('D')@140329230661784> >>> m.get_state('A').name # Gets state name 'A' ('D') 'D' >>> # The following generates an error, since the state isn't renamed in the machine >>> m.get_state('D') # (ValueError: State 'D' is not a registered state.) ValueError: State 'D' is not a registered state. 的所有引用都更改为('A'),因此例如机器('D')的转换将为m

这是可能的,还是我必须坚持添加/删除状态及其所有过渡参考?如果是这样的话,那么可行的方法是什么?

1 个答案:

答案 0 :(得分:0)

简短回答:这并不容易。最稳定的方式是动态调整的外部名称映射。

names = {"A": "stateA", "B": "stateB"}
names["A"] = "stateC"

这也可以在State的子类中完成,其中State.name保留给机器逻辑,State.system_name可以在不造成伤害的情况下进行更改。

如果您想/需要实际替换州名。请继续阅读。

更长的回答:转换管理OrderedDict中的状态,其中键代表州名。此外,在列表的字典中的事件中收集转换,同样将源状态名称作为键。这些事件作为一个名为过渡触发器的方法绑定到模型。转换本身包含属性sourcedest,它们包含它们的起点和终点。等待它......状态名称。 如果启用了自动转换,我们还具有分配给模型的to_<state.name>个函数。与包括on_enter/exit_<state.name>的模型有更多的州名相关纠缠。这意味着有许多地方需要更换字符串。

  

哪里有遗嘱,就有办法。 - 某处的Python编码器

您可以深入挖掘并重命名与状态相关的所有内容,但这会有一些警告。最明显的一个:如果您的Model类定义了on_enter_A,则不能只重命名实例中的方法。在状态实例的初始化期间,它已被添加到状态。您可以从State.on_enter/exit属性中过滤方法,也可以将其分配。这也意味着如果您将状态从“A”重命名为“B”,即使模型中存在on_enter_B方法,也不会重新分配回调。

from transitions import Machine
from collections import OrderedDict


def rename_state(machine, old, new):
    # rename transitions
    for trigger, event in machine.events.items():
        if old in event.transitions:
            trs = event.transitions.pop(old)
            for tr in trs:
                tr.source = new
            event.transitions[new] = trs
        for trs in event.transitions.values():
            for tr in trs:
                if tr.dest == old:
                    tr.dest = new

    if "to_{0}".format(old) in machine.events:
        ev = machine.events.pop("to_{0}".format(old))
        machine.events["to_{0}".format(new)] = ev
    # rename model properties
    for mo in machine.models:
        func = getattr(mo, 'to_' + old, False)
        if func:
            setattr(mo, 'to_' + new, func)
            delattr(mo, 'to_' + old)
        if mo.state == old:
            mo.state = new
    # rename state and reconstruct ordered dict for states
    machine.states[old].name = new
    machine.states = OrderedDict((new if k == old else k, v) for k, v in machine.states.viewitems())


class Model:

    def on_exit_A(self):
        print("Exited A!")

    def on_exit_B(self):
        print("Exited B")

model = Model()
m = Machine(model, initial='A')
m.add_state("A")
m.add_state("C")
m.add_transition('go', 'A', 'C')
rename_state(m, "A", "B")
print model.state  # >>> B
model.go()  # >>> Exited A!
print model.state  # >>> C
print m.get_state('B')

是否有更好的替代品?

您可以从机器中删除转场,但是如果您计划删除并重新添加状态和转场,则必须以困难的方式过滤掉状态。

您还可以拥有一个转换列表和您保存在某处的状态。如果您需要重命名状态,可以更改列表并创建新计算机:

from transitions import Machine

states = ["A", "B"]
transitions = [['ab', 'A', 'B'], ['ba', 'B', 'A']]


class Model():
    pass

model = Model()

m = Machine(model, states=states, transitions=transitions, initial='A')
model.ab()
print model.state  # >>> B

# rename states and transitions
states = [s if s != "A" else "C" for s in states]
transitions = [[t, s if s != "A" else "C", d if d != "A" else "C"] for t, s, d in transitions]

m = Machine(model, states=states, transitions=transitions, initial=model.state)
model.ba()
print model.state  # >>> C

然而,这也包含风险。如前所述,机器为模型分配了某些功能。如果无法重新初始化模型,则至少会为模型分配一些损坏的自动过渡。尝试在第二个示例中调用model.to_A()来查看我的意思。