2010-08-10 3 views
2

Bien que la question soit très spécifique, j'apprécierais aussi vraiment les conseils généraux et d'autres approches qui rendraient ma question discutable. Je construis une collection de programmes AI, et beaucoup de fonctions et de classes doivent gérer beaucoup d'états et d'actions différents qui provoquent des transitions entre les états, donc j'ai besoin d'un moyen de représenter les états et les actions. Veuillez noter que je ne construis pas une machine à états simple, mais plutôt un certain nombre de programmes différents (agents) qui prennent tous des états et retournent des actions comme un moyen d'interagir avec un environnement.Comment ajouter par programme des liaisons à la portée de classe actuelle en Python?

Je pourrais utiliser des chaînes, mais c'est compliqué si un algorithme particulier doit associer des informations supplémentaires avec un état ou une action, et comparer les chaînes encore et encore dans des programmes de longue durée est un gaspillage inutile. Les mêmes types de problèmes se posent avec d'autres types de constantes. Donc, mon idée initiale est d'utiliser des classes imbriquées, comme ceci:

class DerivedAgent(Agent): 
    class StateA(State): pass 
    class StateB(State): pass 
    ... 
    def do_something(state): 
     if state is self.StateA: 
      ... 

Cela fonctionne assez bien, mais s'il y a un certain nombre d'états et d'actions, il peut prendre beaucoup d'espace pour les déclarer, et toutes les déclarations de pass sont ennuyantes. Je voudrais pouvoir faire quelque chose comme ...

class DerivedAgent(Agent): 
    states("StateA", "StateB", "StateC", ...) 

Mais je ne vois pas un moyen d'avoir la méthode states ajouter les types nouvellement créés à la classe DerivedAgent. Je pense que je pourrais être en mesure de le faire avec le module inspect, mais cela semble aller trop loin pour un petit confort. Utiliser des types comme ça est une mauvaise idée? Y a-t-il une approche beaucoup plus élégante? Le code en dehors des classes d'agent doit pouvoir accéder aux états et aux actions, et placer des états dans l'espace de nom du module n'est pas une bonne option car un module donné peut contenir plusieurs agents.

Répondre

2

Vous pouvez utiliser des classes méta afin que vous finiriez avec code comme:

class DerivedAgent(Agent): 
    __states__ = ['StateA', 'StateB', ...] 

par exemple :

class AgentMeta(type): 
    def __new__(meta, classname, bases, classdict): 
     for clsname in classdict['__states__']: 
      classdict[clsname] = type(clsname, (State,), {}) 
     return type.__new__(meta, classname, bases, classdict)) 

puis, juste ewrite votre Agent classe afin qu'il ait la ligne

#python3.x 
class Agent(Base1, Base2, ..., BaseN, metaclass=AgentMeta): 
    #everything else unchanged 

# 2.2 <= python <= 2.7 
class Agent(Base1, Base2, ..., BaseN): 
    __metaclass__ = AgentMeta 
    #everything else unchanged 

Si vous ne voulez pas changer la classe Agent, vous pouvez simplement inclure la déclaration approriate de métaclasse dans chaque sous-classe de ce que vous créez.

+0

C'est exactement le genre de truc que j'espérais pouvoir exister. Merci de l'avoir signalé. – Shawn

+0

content d'avoir pu aider. Vous pourriez vouloir ajouter une vérification pour vous assurer que '__states__' existe dans classdict avant d'essayer de le lire;) J'ai oublié cette partie. ou vous pouvez simplement faire 'pour clsname dans classdict.get ('__ states__', []):' – aaronasterling

0

Si vous voulez une machine d'état, créez-en une. Une machine d'état est pilotée par les données, ce qui signifie que les états et les transitions sont codés en tant que données, et non en tant que hiérarchie de classe.

En Python, les dictionnaires sont le mécanisme de ad hoc expédition polymorphes:

def do_foo(**kwargs): 
    pass 

def do_bar(**kwargs): 
    pass 

dispatch = { 
    # state : { (transition, next_state) ... } 
    0: {'a' : (do_foo, 1)}, 
    1: {'a' : (do_bar, 0)}, 
    1: {'b' : (do_bar, None)}, # None -> accept 
} 

def state_machine(state, input): 
    """does the action corresponding to state on input and returns new state""" 
    current = dispatch[state] 
    if input in current: 
     functor, next = current[input] 
     functor(lexeme=input) 
     return next 

state = 0 
for c in 'aaab': 
    state = state_machine(state, c) 
    if state is None: 
     print 'accepted' 
     break 
+0

Je ne veux pas de machine à états, mais vous fournissez un bon exemple de la façon dont on pourrait être implémenté en Python.Je n'ai rien dit à propos des hiérarchies de classes; Je pense plutôt à utiliser des classes comme données pour encoder l'état, tout comme vous utilisez des entiers pour faire la même chose dans votre implémentation d'une machine d'état. – Shawn

1

Les machines à états explicites sont ennuyeuses, vous pouvez avoir des machines à états implicites dans les coroutines. Mais c'est probablement trop en ce moment. En tout cas class StateA(State): pass est exactement le même que StateA = type("StateA", (State,), {}). Enregistre que vous tapez le pass ;-)

+0

+1 pour l'assistance: J'avais oublié que les états étaient supposés hériter de 'State' – aaronasterling

Questions connexes