Как реализовать Декоратор с нелокальным равенством?
-
02-07-2019 - |
Вопрос
Приветствую, сейчас я занимаюсь рефакторингом одной из своих программ и обнаружил интересную проблему.
У меня есть Переходы в автоматах.Переходы всегда имеют начальное и конечное состояния.Некоторые переходы имеют метку, которая кодирует определенное действие, которое необходимо выполнить при обходе.Отсутствие ярлыка означает отсутствие действий.У некоторых переходов есть условие, которое должно быть выполнено для прохождения этого условия. Если условие отсутствует, переход по сути является эпсилон-переходом в 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
сорт?Я думаю, это внесет ясность.