""" Privacy for attributes fetched from class instances. See self-test code at end of file for a usage example. Decorator same as: Doubler = Private('data', 'size')(Doubler). Private returns onDecorator, onDecorator returns onInstance, and each onInstance instance embeds a Doubler instance. """ traceMe = False def trace(*args): if traceMe: print '[' + ' '.join(map(str, args)) + ']' def Private(*privates): # privates in enclosing scope def onDecorator(aClass): # aClass in enclosing scope class onInstance: # wrapped in instance attribute def __init__(self, *args, **kargs): self.wrapped = aClass(*args, **kargs) def __getattr__(self, attr): # my attrs don't call getattr trace('get:', attr) # others assumed in wrapped if attr in privates: raise TypeError, 'private attribute fetch: ' + attr else: return getattr(self.wrapped, attr) def __setattr__(self, attr, value): # outside accesses trace('set:', attr, value) # others via getattr if attr == 'wrapped': # allow my attrs self.__dict__[attr] = value # avoid looping! elif attr in privates: raise TypeError, 'private attribute change: ' + attr else: setattr(self.wrapped, attr, value) # wrapped obj attrs return onInstance # or use __dict__ return onDecorator if __name__ == '__main__': traceMe = True @Private('data', 'size') class Doubler: def __init__(self, label, start): self.label = label # access inside the subject class self.data = start # not intercepted: run normally def size(self): return len(self.data) # methods run with no checking def double(self): # because privacy not inherited for i in range(self.size()): self.data[i] = self.data[i] * 2 def display(self): print self.label, '=>', self.data X = Doubler('X is', [1, 2, 3]) Y = Doubler('Y is', [-10, -20, -30]) # the followng all succeed print X.label # accesses outside the subject class X.display(); X.double(); X.display() # intercepted: validated, delegated print Y.label Y.display(); Y.double() Y.label = 'Spam =>' Y.display() # the following all fail properly """ print X.size() print X.data X.data = [1, 1, 1] X.size = lambda S: 0 print Y.data print Y.size() """