Как реализовать Декоратор с нелокальным равенством?

StackOverflow https://stackoverflow.com/questions/127736

Вопрос

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

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

Мне нужны следующие операции:

  • проверьте, есть ли у перехода метка
  • возьми этот ярлык
  • добавить метку к переходу
  • проверьте, есть ли у перехода условие
  • получить это условие
  • проверить на равенство

Судя по первым пяти пунктам, это похоже на чистый декоратор с базовым переходом и двумя декораторами:Маркировка и состояние.Однако у этого подхода есть проблема:два перехода считаются равными, если их начальное и конечное состояние одинаковы, метки на обоих переходах равны (или не существуют) и оба условия одинаковы (или не существуют).С помощью декоратора я мог бы иметь два перехода: Labeled("foo", Conditional("bar", Transition("baz", "qux"))) и Conditional("bar", Labeled("foo", Transition("baz ", "qux"))) которым необходимо нелокальное равенство, то есть декораторам нужно будет собрать все данные, а Переход должен сравнить эти собранные данные на базе set:

class Transition(object):
    def __init__(self, start, end):
        self.start = start
        self.end = end
    def get_label(self):
        return None
    def has_label(self):
        return False
    def collect_decorations(self, decorations):
        return decorations
    def internal_equality(self, my_decorations, other):
        try:
            return (self.start == other.start
                    and self.end == other.end
                    and my_decorations = other.collect_decorations())
    def __eq__(self, other):
        return self.internal_equality(self.collect_decorations({}), other)

class Labeled(object):
    def __init__(self, label, base):
        self.base = base
        self.label = label
    def has_label(self):
        return True
    def get_label(self):
        return self.label
    def collect_decorations(self, decorations):
        assert 'label' not in decorations
        decorations['label'] = self.label
        return self.base.collect_decorations(decorations)
    def __getattr__(self, attribute):
        return self.base.__getattr(attribute)

Это чистый подход?Я что-то пропустил?

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

class Transition(object):
    def __init__(self, **kwargs):
        # init is pythons MI-madness ;-)
        super(Transition, self).__init__(**kwargs)
        self.start = kwargs['start']
        self.end = kwargs['end']
    def get_label(self):
        return None
    def get_condition(self):
        return None
    def __eq__(self, other):
        try:
            return self.start == other.start and self.end == other.end
        except AttributeError:
            return False

class LabeledTransition(Transition):
    def __init__(self, **kwargs):
        super(LabeledTransition).__init__(**kwargs)
        self.label = kwargs['label']
    def get_label(self):
        return self.label
    def __eq__(self):
        super_result = super(LabeledTransition, self).__eq__(other)
        try:
            return super_result and self.label == other.label
        except AttributeError:
            return False

class ConditionalTransition(Transition):
    def __init__(self, **kwargs):
        super(ConditionalTransition, self).__init__(**kwargs)
        self.condition = kwargs['condition']

    def get_condition(self):
        return self.condition

    def __eq__(self, other):
        super_result = super(ConditionalTransition, self).__eq__(other)
        try:
            return super_result and self.condition = other.condition
        except AttributeError:
            return False

# ConditionalTransition about the same, with get_condition
class LabeledConditionalTransition(LabeledTransition, ConditionalTransition):
    pass

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

Конечно, третьим вариантом было бы просто забить все в один класс перехода с кучей has_label/has_transition.

Так...Я смущен.Я что-то пропустил?Какая реализация выглядит лучше?Как вы справляетесь с подобными случаями, то есть с объектами, которые выглядят так, будто их может обрабатывать Декоратор, но потом появляется такой нелокальный метод?

РЕДАКТИРОВАТЬ:Добавлен класс ConditionalTransition.По сути, этот вид ведет себя как декоратор, за вычетом порядка, созданного порядком создания декораторов, перехода проверяет правильность начала и конца, класс LabeledTransition проверяет правильность метки, а ConditionalTransition проверяет правильность условия.

Это было полезно?

Решение

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

class State(object):
    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return self.name

class Automaton(object):
    def __init__(self, instance, start):
        self._state = start
        self.transitions = instance.transitions()

    def get_state(self):
        return self._state

    def set_state(self, target):
        transition = self.transitions.get((self.state, target))
        if transition:
            action, condition = transition
            if condition:
                if condition():
                    if action:
                        action()
                    self._state = target
            else:
                self._state = target
        else:
            self._state = target

    state = property(get_state, set_state)

class Door(object):
    open = State('open')
    closed = State('closed')

    def __init__(self, blocked=False):
        self.blocked = blocked

    def close(self):
        print 'closing door'

    def do_open(self):
        print 'opening door'

    def not_blocked(self):
        return not self.blocked

    def transitions(self):
        return {
            (self.open, self.closed):(self.close, self.not_blocked),
            (self.closed, self.open):(self.do_open, self.not_blocked),
        }

if __name__ == '__main__':
    door = Door()
    automaton = Automaton(door, door.open)

    print 'door is', automaton.state
    automaton.state = door.closed
    print 'door is', automaton.state
    automaton.state = door.open
    print 'door is', automaton.state
    door.blocked = True
    automaton.state = door.closed
    print 'door is', automaton.state

вывод этой программы будет:

door is open
closing door
door is closed
opening door
door is open
door is open

Другие советы

Судя по опубликованному коду, единственная разница между Transition и Labeled Transition — это возврат get_lable() и has_label().В этом случае вы можете сжать эти два в один класс, который устанавливает атрибут метки на None и

return self.label is not None

в функции has_label().

Можете ли вы опубликовать код для ConditionalTransition сорт?Я думаю, это внесет ясность.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top