Библиотека переходов Python — вызов перехода из внутренних обратных вызовов

#python #pytransitions

Вопрос:

Я использую библиотеку переходов для Python и нахожу ее действительно полезной. В моем конкретном случае я использую FSMS, которые принимают решения внутри состояний (в обратных вызовах) и соответственно внутренней логике вызывают новые переходы. Например:

 states = ["Begin", "CheckCondition", "MakeA", "MakeB", "End"]

transitions = [
    ["proceed", "Begin", "CheckCondition"],
    ["path_a", "CheckCondition", "MakeA"],
    ["path_b", "CheckCondition", "MakeB"],
    ["proceed", "MakeA", "End"],
    ["proceed", "MakeB", "End"]
]

class MyModel:
    def __str__(self):
        return f">>>>> STATE: {self.state}"

    def on_enter_CheckCondition(self):
        if random.randint(1, 10) > 5:
            self.path_a()
            return
        else:
            self.path_b()
            return

my_model = MyModel()

machine = HierarchicalGraphMachine(
    model=my_model,
    states=states,
    transitions=transitions,
    initial="Begin",
    ignore_invalid_triggers=False
)

while my_model.state != "End":
    my_model.proceed()
 

Этот подход сохраняет всю логику внутри FSM, это то, что я хочу, но имеет 2 недостатка:

  • У меня потенциально может быть бесконечная цепочка вызовов, которая вызовет переполнение стека (только proceed() переход, вызываемый while циклом, прерывает его).
  • Я должен помнить, что нужно звонить вручную return после вызова любого перехода с обратного вызова

Есть ли лучший способ использовать библиотеку переходов для достижения того, чего я хочу (вызвать переход из обратного вызова)?

Спасибо за помощь!

Ответ №1:

transitions поддерживает условные переходы, при которых вы можете передать проверку подлинности в качестве обратного вызова для этого перехода. Вы можете довольно легко смоделировать альтернативные пути, предоставив список переходов с различными условиями. Обратите внимание, что переходы оцениваются в том порядке, в котором они были добавлены. Для вашего примера это означает, что вы могли бы написать его следующим образом:

 from transitions import Machine
import random

# we do not need 'CheckConditions' when using 'conditions' in transitions
states = ["Begin", "MakeA", "MakeB", "End"]

transitions = [
    # when 'proceed' is triggered, 'is_larger_than_5' will be evaluated. The transition is only
    # conducted when it evaluates to True.
    {"trigger": "proceed", "source": "Begin", "dest": "MakeA", "conditions": ["is_larger_than_5"]},
    # This one would be evaluated next. You could add as many checks and paths as you like.
    # {"trigger": "proceed", "source": "Begin", "dest": "MakeX", "conditions": ["condition_check"]},
    # Otherwise this transition is executed. Since it has no 'conditions' it acts as an 'else' clause.
    {"trigger": "proceed", "source": "Begin", "dest": "MakeB"},
    # You can specify multiple sources for each transition.
    ["proceed", ["MakeA", "MakeB"], "End"],
]


class MyModel:
    def __str__(self):
        return f">>>>> STATE: {self.state}"

    # our condition
    @staticmethod
    def is_larger_than_5():
        return random.randint(1, 10) > 5


my_model = MyModel()

machine = Machine(
    model=my_model,
    states=states,
    transitions=transitions,
    initial="Begin",
    ignore_invalid_triggers=False
)

while my_model.state != "End":
    my_model.proceed()
    print(my_model)
 

Это вернется либо:

 >>>>> STATE: MakeB
>>>>> STATE: End
 

или:

 >>>>> STATE: MakeA
>>>>> STATE: End
 

Ответ №2:

Условные переходы поддерживаются переходами, и вы можете передать проверку правильности в качестве обратного вызова для этого перехода. Предоставляя список переходов с различными обстоятельствами, вы можете легко моделировать различные пути. Переходы оцениваются в том порядке, в котором они были добавлены.

Комментарии:

1. Пожалуйста, укажите дополнительную информацию в своем ответе. Как это написано в настоящее время, трудно понять ваше решение.