Дизайн конечного автомата Python - PullRequest
45 голосов
/ 20 января 2010

Относительно этого вопроса о переполнении стека (проектирование машины состояния C) , не могли бы вы, ребята из Stack Overflow, поделились со мной своими методами проектирования машины состояния Python (и сообществом)?

На данный момент я собираюсь на двигатель, основанный на следующем:

class TrackInfoHandler(object):
    def __init__(self):
        self._state="begin"
        self._acc=""

    ## ================================== Event callbacks

    def startElement(self, name, attrs):
        self._dispatch(("startElement", name, attrs))

    def characters(self, ch):
        self._acc+=ch

    def endElement(self, name):
        self._dispatch(("endElement", self._acc))
        self._acc=""

    ## ===================================
    def _missingState(self, _event):
        raise HandlerException("missing state(%s)" % self._state)

    def _dispatch(self, event):
        methodName="st_"+self._state
        getattr(self, methodName, self._missingState)(event)

    ## =================================== State related callbacks

Но я уверен, что есть множество способов достичь этого, используя динамический характер Python (например, динамическую диспетчеризацию).

Я использую методы проектирования для "двигателя", который получает "события" и "отправляет" против тех, которые основаны на "состоянии" машины.

Ответы [ 12 ]

0 голосов
/ 07 июня 2019

Вот решение для "statefull объектов", которое я придумала, но оно довольно неэффективно для вашей предполагаемой цели, потому что изменения состояния относительно дороги. Однако это может хорошо работать для объектов, которые изменяют состояние нечасто или подвергаются лишь ограниченному числу изменений состояния. Преимущество состоит в том, что после изменения состояния избыточная косвенность отсутствует.

class T:
    """
    Descendant of `object` that rectifies `__new__` overriding.

    This class is intended to be listed as the last base class (just
    before the implicit `object`).  It is a part of a workaround for

      * https://bugs.python.org/issue36827
    """

    @staticmethod
    def __new__(cls, *_args, **_kwargs):
        return object.__new__(cls)

class Stateful:
    """
    Abstract base class (or mixin) for "stateful" classes.

    Subclasses must implement `InitState` mixin.
    """

    @staticmethod
    def __new__(cls, *args, **kwargs):
        # XXX: see https://stackoverflow.com/a/9639512
        class CurrentStateProxy(cls.InitState):
            @staticmethod
            def _set_state(state_cls=cls.InitState):
                __class__.__bases__ = (state_cls,)

        class Eigenclass(CurrentStateProxy, cls):
            __new__ = None  # just in case

        return super(__class__, cls).__new__(Eigenclass, *args, **kwargs)

# XXX: see https://bugs.python.org/issue36827 for the reason for `T`.
class StatefulThing(Stateful, T):
    class StateA:
        """First state mixin."""

        def say_hello(self):
            self._say("Hello!")
            self.hello_count += 1
            self._set_state(self.StateB)
            return True

        def say_goodbye(self):
            self._say("Another goodbye?")
            return False

    class StateB:
        """Second state mixin."""

        def say_hello(self):
            self._say("Another hello?")
            return False

        def say_goodbye(self):
            self._say("Goodbye!")
            self.goodbye_count += 1
            self._set_state(self.StateA)
            return True

    # This one is required by `Stateful`.
    class InitState(StateA):
        """Third state mixin -- the initial state."""

        def say_goodbye(self):
            self._say("Why?")
            return False

    def __init__(self, name):
        self.name = name
        self.hello_count = self.goodbye_count = 0

    def _say(self, message):
        print("{}: {}".format(self.name, message))

    def say_hello_followed_by_goodbye(self):
        self.say_hello() and self.say_goodbye()

# ----------
# ## Demo ##
# ----------
if __name__ == "__main__":
    t1 = StatefulThing("t1")
    t2 = StatefulThing("t2")
    print("> t1, say hello.")
    t1.say_hello()
    print("> t2, say goodbye.")
    t2.say_goodbye()
    print("> t2, say hello.")
    t2.say_hello()
    print("> t1, say hello.")
    t1.say_hello()
    print("> t1, say hello followed by goodbye.")
    t1.say_hello_followed_by_goodbye()
    print("> t2, say goodbye.")
    t2.say_goodbye()
    print("> t2, say hello followed by goodbye.")
    t2.say_hello_followed_by_goodbye()
    print("> t1, say goodbye.")
    t1.say_goodbye()
    print("> t2, say hello.")
    t2.say_hello()
    print("---")
    print( "t1 said {} hellos and {} goodbyes."
           .format(t1.hello_count, t1.goodbye_count) )
    print( "t2 said {} hellos and {} goodbyes."
           .format(t2.hello_count, t2.goodbye_count) )

    # Expected output:
    #
    #     > t1, say hello.
    #     t1: Hello!
    #     > t2, say goodbye.
    #     t2: Why?
    #     > t2, say hello.
    #     t2: Hello!
    #     > t1, say hello.
    #     t1: Another hello?
    #     > t1, say hello followed by goodbye.
    #     t1: Another hello?
    #     > t2, say goodbye.
    #     t2: Goodbye!
    #     > t2, say hello followed by goodbye.
    #     t2: Hello!
    #     t2: Goodbye!
    #     > t1, say goodbye.
    #     t1: Goodbye!
    #     > t2, say hello.
    #     t2: Hello!
    #     ---
    #     t1 said 1 hellos and 1 goodbyes.
    #     t2 said 3 hellos and 2 goodbyes.

Я разместил «запрос на замечания» здесь .

0 голосов
/ 05 июня 2013

Другие связанные проекты:

Вы можете нарисовать конечный автомат, а затем использовать его в своем коде.

...