Come implementare un decoratore con uguaglianza non locale?
-
02-07-2019 - |
Domanda
Saluti, attualmente sto eseguendo il refactoring di uno dei miei programmi e ho riscontrato un problema interessante.
Ho Transizioni in automi. Le transizioni hanno sempre uno stato iniziale e uno finale. Alcune Transizioni hanno un'etichetta, che codifica per una determinata Azione che deve essere eseguita su attraversamento. Nessuna etichetta significa nessuna azione. Alcune transizioni hanno una condizione, che deve essere soddisfatta per attraversare questa condizione, se non c'è alcuna condizione, la transizione è fondamentalmente una transizione epsilon in un NFA e sarà attraversata senza consumare un simbolo di input.
Ho bisogno delle seguenti operazioni:
- controlla se la transizione ha un'etichetta
- ottieni questa etichetta
- aggiungi un'etichetta a una transizione
- controlla se la transizione ha una condizione
- ottieni questa condizione
- verifica l'uguaglianza
A giudicare dai primi cinque punti, questo suona come un chiaro decoratore, con una transizione di base e due decoratori: Etichetta e Condizione. Tuttavia, questo approccio ha un problema: due transizioni sono considerate uguali se il loro stato iniziale e finale sono uguali, le etichette in entrambe le transizioni sono uguali (o inesistenti) ed entrambe le condizioni sono uguali (o non esistenti) . Con un decoratore, potrei avere due transizioni con etichetta (" foo " ;, condizionale (" bar " ;, transizione (" baz " ;, " qux "))) e condizionale (" bar " ;, con etichetta (" foo " ;, Transizione ("quotaz", "quot" qux "))) che necessitano di un'uguaglianza non locale, ovvero i decoratori dovrebbero raccogliere tutti i dati e la transizione deve confrontare questi dati raccolti su un 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)
È un approccio pulito? Mi sto perdendo qualcosa?
Sono per lo più confuso, perché posso risolverlo - con nomi di classe più lunghi - usando l'ereditarietà multipla 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
la classe LabledConditionalTransition si comporta esattamente come previsto - e non avere codice all'interno è allettante e non credo che MI sia confuso a queste dimensioni.
Naturalmente, la terza opzione sarebbe quella di trasformare tutto in una singola classe di transizione con un gruppo di in has_label / has_transition.
Quindi ... sono confuso. Mi sto perdendo qualcosa? Quale implementazione sembra migliore? Come gestite casi simili, ovvero oggetti che sembrano Decoratori potrebbero gestirli, ma poi viene fuori un metodo non locale?
Modifica : Aggiunta la classe ConditionalTransition. Fondamentalmente, questo tipo di comportamento si comporta come il decoratore, meno l'ordine creato dall'ordine di creazione dei decoratori, i controlli di transizione per l'inizio e la fine sono corretti, la classe LabeledTransition verifica che l'etichetta sia corretta e ConditionalTransition controlla che la condizione sia corretta.
Soluzione
Penso sia chiaro che nessuno capisce davvero la tua domanda. Suggerirei di inserirlo nel contesto e di renderlo più breve. Ad esempio, ecco una possibile implementazione del modello di stato in Python, per favore studiatelo per avere un'idea.
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
l'output di questo programma sarebbe:
door is open
closing door
door is closed
opening door
door is open
door is open
Altri suggerimenti
Dal codice che è stato pubblicato, l'unica differenza tra Transizione e Transizione con etichetta è il ritorno di get_lable () e has_label (). Nel qual caso puoi comprimere questi due una singola classe che imposta un attributo label su None e
return self.label is not None
nella funzione has_label ().
Puoi pubblicare il codice per la classe ConditionalTransition
? Penso che ciò renderebbe più chiaro.