Python has no inherent provision for a restrictive API that blocks accesses to methods and variables outside an allowed set. Inexperienced Python programmers may fail to adhere to an agreed-upon API, directly accessing the private internals of a class. Adherence to defined APIs is a good thing. This function allows a class to specify its API, and raise AttributeErrors for disallowed accesses.
def restrictiveApi(klas): class newklas: def __init__(self, *args): self.__inst = apply(klas, args) def __getattr__(self, attr): # If the attribute is in the permitted API, then return # the correct thing, no matter who asks for it. # if attr in self.__inst._PUBLIC: return getattr(self.__inst, attr) # If the attribute is outside the permitted API, then # return it only if the calling class is in the list of # friends. Otherwise raise an AttributeError. # elif hasattr(self.__inst, '_FRIENDS'): # find the class of the method that called us try: raise Exception except: import sys tb = sys.exc_info()[2] callerClass = tb.tb_frame.f_back.\ f_locals['self'].__class__ # if it's a friend class, return the requested thing if callerClass.__name__ in self.__inst._FRIENDS: return getattr(self.__inst, attr) # if not a friend, raise an AttributeError raise AttributeError, attr return newklas To use this, a class needs to define two class variables, _PUBLIC and _FRIENDS, both being lists (or tuples) of strings. The _PUBLIC list gives the names of all methods and variables that should be considered public, i.e. any other class may use them. The _FRIENDS list gives the names of classes that are allowed free access to all methods and variables in the protected class. The _FRIENDS list is optional. Having defined _PUBLIC and optionally _FRIENDS, use something like the following to protect your class. Restricting the API will incur a performance overhead, so it's best to do it under the control of some sort of debug flag. if debug_flag: from restrictive import restrictiveApi MyClass = restrictiveApi(MyClass) ======== Examples ========== class ClassUnderTest: # This class has a private variable called privateX. It can be # set using the setX() method or gotten using the x() method. # If another class appears in the _FRIENDS list, that class # can access privateX directly. # _PUBLIC = ('x', 'setX') _FRIENDS = ('FriendClass',) def __init__(self, x): # __init__ is always callable by anybody self.setX(x) def x(self): # callable by anybody return self.privateX def setX(self, x): # callable by anybody self.privateX = x ClassUnderTest = restrictiveApi(ClassUnderTest) class FriendClass: def getX(self, cut): return cut.privateX # this works fine class StrangerClass: def getX(self, cut): return cut.privateX # this raises an AttributeError -- http://mail.python.org/mailman/listinfo/python-list