質問

I want to create a structure with a messenger class which is shared between several other classes. I want to keep parameters synchronized between the messenger and the sub-classes, and I also want the sub-classes to be able to access each other via the messenger. I have currently implemented it as follows, but am having some issues with synchronization:

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)

I am guessing that this discrepancy results from a lack of understanding of when things are stored by reference vs. by value in Python, or maybe some weird loophole in the getattr override. Maybe I could add some sort of synchronization function to ensure that everything stays synced up, but it already feels pretty hackish and I would think that there is a cleaner solution. The end goal is to have a messenger class and sub-classes which interact transparently with that messenger to create/store attributes and keep everything synchronized.

Note that although this example does not show it, the actual application also requires that the sub-classes have access to each other's methods (as well as the messenger's methods). Not sure if this makes any difference or not.

Any ideas? Am I on the right track or should I do something totally different? I looked into metaclasses and decorators, but couldn't immediately see if/how they would help me.

役に立ちましたか?

解決

Have look at the instance attributes:

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 always looks in the instance dict first. It finds 'paramA' in A.__dict__. If it would not be there it would go up to the class and would call your __getattr__ because you force it with __getattribute__.

Accordingly, preventing the setting into the instance dict would change the behavior. When you write self.paramA = total you call this method:

 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

The very last line self.__dict__[attr] = val puts the attribute into the instance dict. Comment out this line and you get the output you expect. If this makes sense for the problem at hand is a different question though.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top