Дизайн конечного автомата Python

Связанный с этим вопросом о Переполнении стека (C дизайн конечного автомата), мог Вы люди Переполнения стека совместно использовать свои методы проектирования конечного автомата 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 (например, динамическая диспетчеризация).

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

45
задан Peter Mortensen 3 July 2018 в 23:53
поделиться

6 ответов

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

class SuperState( object ):
    def someStatefulMethod( self ):
        raise NotImplementedError()
    def transitionRule( self, input ):
        raise NotImplementedError()

class SomeState( SuperState ):
    def someStatefulMethod( self ):
        actually do something()
    def transitionRule( self, input ):
        return NextState()

Это довольно общая котельная, используемая в Java, C ++, Python (и я уверен, что другие языки, также).

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

Обратите внимание, что нам нужно иметь прямые ссылки, поэтому мы ссылаемся на классы по имени, и используем EVAL , чтобы перевести имя класса в реальный класс. Альтернатива состоит в том, чтобы сделать переменные экземпляра правил перехода вместо переменных классов, а затем создавать экземпляры после определения всех классов.

class State( object ):
    def transitionRule( self, input ):
        return eval(self.map[input])()

class S1( State ): 
    map = { "input": "S2", "other": "S3" }
    pass # Overrides to state-specific methods

class S2( State ):
    map = { "foo": "S1", "bar": "S2" }

class S3( State ):
    map = { "quux": "S1" }

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

class State( object ):
    def transitionRule( self, input ):
        next_states = [ s for f,s in self.map if f(input)  ]
        assert len(next_states) >= 1, "faulty transition rule"
        return eval(next_states[0])()

class S1( State ):
    map = [ (lambda x: x == "input", "S2"), (lambda x: x == "other", "S3" ) ]

class S2( State ):
    map = [ (lambda x: "bar" <= x <= "foo", "S3"), (lambda x: True, "S1") ]

Поскольку правила оцениваются последовательно, это позволяет «по умолчанию» правило.

39
ответ дан 26 November 2019 в 21:19
поделиться

Есть эта модель дизайна для использования декораторов для реализации состоятельных машин. Из описания на странице:

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

Есть пример кода на странице, а также довольно долго, поэтому я не буду вставлять его здесь).

8
ответ дан 26 November 2019 в 21:19
поделиться

В апреле 2009 года выпуск журнала Python я написал статью о встраивании государственного DSL внутри Python, используя Pyparding и Imputil. Этот код позволит вам написать модуль FractyLight.Pystate:

# trafficLight.pystate

# define state machine
statemachine TrafficLight:
    Red -> Green
    Green -> Yellow
    Yellow -> Red

# define some class level constants
Red.carsCanGo = False
Yellow.carsCanGo = True
Green.carsCanGo = True

Red.delay = wait(20)
Yellow.delay = wait(3)
Green.delay = wait(15)

и компилятор DSL создаст весь необходимый трафик, красный, желтый и зеленый классы, а также правильные методы передачи состояния. Код может вызвать эти классы, используя что-то вроде этого:

import statemachine
import trafficLight

tl = trafficLight.Red()
for i in range(6):
    print tl, "GO" if tl.carsCanGo else "STOP"
    tl.delay()
    tl = tl.next_state()

(к сожалению, иммутил был сброшен в Python 3.)

12
ответ дан 26 November 2019 в 21:19
поделиться

Я думаю, что ответ S. Lott является гораздо лучшим способом реализации государственного машины, но если вы все еще хотите продолжить свой подход, используя (штат, событие) Как ключ для вашего диктография лучше. Изменение вашего кода:

class HandlerFsm(object):

  _fsm = {
    ("state_a","event"): "next_state",
    #...
  }
3
ответ дан 26 November 2019 в 21:19
поделиться

Это, вероятно, зависит от того, насколько сложна ваша государственная машина. Для простых состоятельных машин, дикт диктографии (из клавиш событий к клавишам для DFAS или ключей событий до списков / наборов / кортежей клавишных ключей для NFA), вероятно, будет самая простая вещь для записи и понимать.

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

2
ответ дан 26 November 2019 в 21:19
поделиться

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

class TrackInfoHandler(object):
    def __init__(self):
        self._stack=[]

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

    def startElement(self, name, attrs):
        cls = self.elementClasses[name]
        self._stack.append(cls(**attrs))

    def characters(self, ch):
        self._stack[-1].addCharacters(ch)

    def endElement(self, name):
        e = self._stack.pop()
        e.close()
        if self._stack:
            self._stack[-1].addElement(e)

Для каждого типа элемента, вам просто нужен класс, который поддерживает методы addCharacters, addElement, и close.

EDIT: Чтобы пояснить, да, я имею в виду, что машины конечных состояний - это, как правило, неправильный ответ, что, как техника программирования общего назначения, они - мусор, и вам следует держаться подальше от них.

Есть несколько действительно хорошо понимаемых, четко определенных проблем, для которых FSM являются хорошим решением. lex , например, хороший материал.

Тем не менее, ФСМ обычно плохо справляются с изменениями. Предположим, когда-нибудь Вы захотите добавить немного состояния, возможно, флаг "мы уже видели элемент X?". В коде выше, Вы добавляете булевый атрибут к соответствующему классу элементов и все. В машине с конечным состоянием вы удваиваете количество состояний и переходов.

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

1
ответ дан 26 November 2019 в 21:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: