2013-05-29 2 views
0

Je souhaite créer une structure avec une classe messenger partagée entre plusieurs autres classes. Je veux garder les paramètres synchronisés entre le messager et les sous-classes, et je veux aussi que les sous-classes puissent accéder les unes aux autres via le messager. J'ai actuellement mis en œuvre comme suit, mais je rencontre quelques problèmes avec la synchronisation:Habillage et synchronisation de classe messenger en python

class MessengerWrapper: 
    def __getattribute__(self,attr): 
     #Force use of custom getattr 
     return self.__getattr__(self,attr) 

    def __getattr__(self,attr): 

     #return the messenger if asking for it 
     if attr == '_messenger': 
      return object.__getattribute__(self,attr) 

     #Return the value of the _messenger instance if its there. 
     elif hasattr(self._messenger,attr): 
      return getattr(self._messenger,attr) 

     #Otherwise return the instances own value 
     else: 
      if attr in self.__dict__: 
       val = object.__getattribute__(self,attr) 
       #keep the messenger synchronized 
       setattr(self._messenger,attr,val) 
       return val 
      else: 
       raise AttributeError() 


    def __setattr__(self,attr,val): 
     #set the messenger if necessary 
     if attr == '_messenger': 
      self.__dict__[attr] = val 
     #otherwise, set the attribute and sync the messenger 
     else: 
      setattr(self._messenger,attr,val) 
      self.__dict__[attr] = val 

class Messenger: 
    def __init__(self): 
     self.param1 = 1 

class SubClassA(MessengerWrapper): 
    def __init__(self,messenger): 
     self._messenger = messenger 
     self.paramA = 2 

class SubClassB(MessengerWrapper): 
    def __init__(self,messenger): 
     self._messenger = messenger 
     self.paramB = 3 

    def function(self): 
     total = self.param1 + self.paramB + self.subA.paramA 
     self.paramA = total 

messenger = Messenger() 
A = SubClassA(messenger) 
B = SubClassB(messenger) 
messenger.subA = A 
messenger.subB = B 

B.function() 

print messenger.paramA, B.paramA 
#-> 6, 6 (as expected) 
print A._messenger.paramA, A.paramA 
#-> 6, 2 (I would expect 6, 6 since A.paramA should return A._messenger.paramA) 

Je devine que ce écart résulte d'un manque de compréhension quand les choses sont stockées par référence par rapport à la valeur en Python, ou peut-être une échappatoire étrange dans le getattr override. Peut-être que je pourrais ajouter une sorte de fonction de synchronisation pour que tout reste synchronisé, mais il me semble déjà assez hackish et je pense qu'il y a une solution plus propre. L'objectif final est d'avoir une classe de messagers et des sous-classes qui interagissent de manière transparente avec ce messager pour créer/stocker des attributs et tout synchroniser.

Notez que bien que cet exemple ne le montre pas, l'application réelle requiert également que les sous-classes aient accès aux méthodes de l'autre (ainsi qu'aux méthodes du messager). Je ne sais pas si cela fait une différence ou non.

Des idées? Suis-je sur la bonne voie ou devrais-je faire quelque chose de totalement différent? J'ai regardé dans les métaclasses et les décorateurs, mais je n'ai pas pu voir immédiatement si/comment ils m'aideraient.

Répondre

1

Demandez regard sur attributs de l'instance:

A.__dict__ {'paramA': 2, '_messenger': <__main__.Messenger instance at 0x100468cf8>} 
A._messenger.__dict__ {'subA': <__main__.SubClassA instance at 0x100468d40>, 
         'subB': <__main__.SubClassB instance at 0x100468d88>, 
         'paramA': 6, 'param1': 1, 'paramB': 3} 

Python cherche toujours l'instance dict premier. Il trouve 'paramA' dans A.__dict__. S'il ne serait pas là il irait jusqu'à la classe et appellerait votre __getattr__ parce que vous le forcez avec __getattribute__. Par conséquent, empêcher le paramétrage dans l'instance changerait le comportement. Lorsque vous écrivez self.paramA = total vous appelez cette méthode:

def __setattr__(self,attr,val): 
    if attr == '_messenger': 
     self.__dict__[attr] = val 
    #otherwise, set the attribute and sync the messenger 
    else: 
     setattr(self._messenger,attr,val) 
     # comment out the following line to get your 6 
     self.__dict__[attr] = val 

La dernière ligne self.__dict__[attr] = val met l'attribut dans le dict d'instance. Faites un commentaire sur cette ligne et vous obtenez la sortie que vous attendez. Si cela a du sens pour le problème à résoudre, c'est une autre question.

+0

Très belle réponse. Je n'ai pas réalisé que python a vérifié '__dict__' avant d'appeler' __getattribute__'. Je peux certainement utiliser cette connaissance dans mon application. –