Como implementar um decorador com a igualdade não-local?
-
02-07-2019 - |
Pergunta
Greetings, neste momento estou a refatoração um dos meus programas, e eu encontrei um problema interessante.
Eu tenho Transitions em um autômato. Transitions sempre tem uma start-estado e um estado final. Algumas transições têm um rótulo, que codifica uma determinada ação que deve ser realizada após a travessia. Nenhum rótulo significa nenhuma ação. Algumas transições têm uma condição que deve ser cumprida, a fim de atravessar esta condição, se não há nenhuma condição, a transição é basicamente um epsilon-transição em uma NFA e será percorrido sem consumir um símbolo de entrada.
Eu preciso as seguintes operações:
- Verifique se a transição tem um rótulo
- essa etiqueta ??li>
- adicionar um rótulo para uma transição
- Verifique se a transição tem uma condição
- obter esta condição
- cheque de igualdade
A julgar pelos primeiros cinco pontos, isso soa como um decorador clara, com uma transição de base e dois decoradores: Etiquetado e condição. No entanto, esta abordagem tem um problema: duas transições são considerados iguais se o seu start-estado e estado final são os mesmos, os rótulos em ambas as transições são iguais (ou não-existente) e ambas as condições são as mesmas (ou não existente) . Com um decorador, eu poderia ter duas transições etiquetados ( "foo", condicional ( "bar", Transição ( "baz", "Qux"))) e condicional ( "bar", identificado como ( "foo", Transição ( "baz ", 'Qux'))), que precisa de uma igualdade não-local, ou seja, os decoradores precisaria coletar todos os dados ea transição deve comparar esses dados coletados em um set-base:
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)
Esta é uma abordagem limpa? Estou faltando alguma coisa?
Estou principalmente confuso, porque eu posso resolver isso - com nomes de classe mais longos - usando herança múltipla cooperativa:
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
se comporta classe LabledConditionalTransition exatamente como esperado -. E não tendo nenhum código lá é atraente e eu não coisa MI é confuso neste tamanho
Claro, a terceira opção seria apenas martelo tudo em uma única classe de transição com um monte de em Has_label / has_transition.
Então ... Estou confuso. Estou esquecendo de algo? Cuja implementação fica melhor? Como você lida com casos semelhantes, ou seja, objetos que se parecem com um Decorator pode lidar com eles, mas, em seguida, um tal método não-local vem por aí?
Editar : Adicionado a ConditionalTransition classe. Basicamente, este comporta-se um bocado como o decorador, menos a ordem criada pela ordem de criação dos decoradores, os controlos de transição para início e fim de ser correta, os controlos LabeledTransition classe para rótulo sendo verificações corretas e ConditionalTransition para a condição de ser correto.
Solução
Eu acho que é claro que ninguém realmente entende a sua pergunta. Eu sugiro colocá-lo em contexto e torná-lo mais curto. Como exemplo, aqui está uma possível implementação do padrão de estado em python, por favor, estudá-lo para ter uma idéia.
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
A saída deste Programa seria:
door is open
closing door
door is closed
opening door
door is open
door is open
Outras dicas
A partir do código que foi publicado, a única diferença entre Transição e Rotulado de Transição é o retorno do get_lable () e Has_label (). Caso em que você pode comprimir esses dois uma única classe que define um atributo label para Nenhum e
return self.label is not None
na função Has_label ().
Você pode postar o código para a classe ConditionalTransition
? Eu acho que isso iria torná-lo mais claro.