Example from "Learning Python, 5th Edition" (copyright Mark Lutz).
Private and Public declarations for instances of classes, via 2.6+
class decorators.  Controls access to attributes stored on an
instance, or inherited by it from its classes.

Private() declares attribute names that cannot be fetched or
assigned outside the subject class, and Public() declares all the
names that can.  See the book for more details; an extra mix-in
class required to handle operator overloading methods correctly in
3.X; and an extension to disable the decorator in -O optimized mode.

traceMe = False
def trace(*args):
    if traceMe: print('[' + ' '.join(map(str, args)) + ']')

def accessControl(failIf):
    def onDecorator(aClass):
        class onInstance:
            def __init__(self, *args, **kargs):
                self.__wrapped = aClass(*args, **kargs)

            def __getattr__(self, attr):
                trace('get:', attr)
                if failIf(attr):
                    raise TypeError('private attribute fetch: ' + attr)
                    return getattr(self.__wrapped, attr)

            def __setattr__(self, attr, value):
                trace('set:', attr, value)
                if attr == '_onInstance__wrapped':
                    self.__dict__[attr] = value
                elif failIf(attr):
                    raise TypeError('private attribute change: ' + attr)
                    setattr(self.__wrapped, attr, value)

        return onInstance
    return onDecorator

def Private(*attributes):
    return accessControl(failIf=(lambda attr: attr in attributes))

def Public(*attributes):
    return accessControl(failIf=(lambda attr: attr not in attributes))

# self-test, example usage

if __name__ == '__main__':
    import sys
    @Private('job', 'pay')
    class Emp1:
        def __init__(self):
   = 'Bob'
            self.job  = 'dev'
    = 50000

    class Emp2:
        def __init__(self):
   = 'Sue'
            self.job  = 'dev'
    = 50000

    x, y = Emp1(), Emp2()
    try:    print(x.job)
    except: print('failed correctly =>', sys.exc_info()[1])
    try:    print(y.job)
    except: print('failed correctly =>', sys.exc_info()[1])

[Home page] Books Code Blog Python Author Train Find ©M.Lutz