I am dissatisfied with the previous two answers to create read only properties because the first solution allows the readonly attribute to be deleted and then set and doesn't block the __dict__. The second solution could be worked around with testing - finding the value that equals what you set it two and changing it eventually.

Now, for the code.

def final(cls): clss = cls @classmethod def __init_subclass__(cls, **kwargs): raise TypeError("type '{}' is not an acceptable base type".format(clss.__name__)) cls.__init_subclass__ = __init_subclass__ return cls def methoddefiner(cls, method_name): for clss in cls.mro(): try: getattr(clss, method_name) return clss except(AttributeError): pass return None def readonlyattributes(*attrs): """Method to create readonly attributes in a class Use as a decorator for a class. This function takes in unlimited string arguments for names of readonly attributes and returns a function to make the readonly attributes readonly. The original class's __getattribute__, __setattr__, and __delattr__ methods are redefined so avoid defining those methods in the decorated class You may create setters and deleters for readonly attributes, however if they are overwritten by the subclass, they lose access to the readonly attributes. Any method which sets or deletes a readonly attribute within the class loses access if overwritten by the subclass besides the __new__ or __init__ constructors. This decorator doesn't support subclassing of these classes """ def classrebuilder(cls): def __getattribute__(self, name): if name == '__dict__': from types import MappingProxyType return MappingProxyType(super(cls, self).__getattribute__('__dict__')) return super(cls, self).__getattribute__(name) def __setattr__(self, name, value): if name == '__dict__' or name in attrs: import inspect stack = inspect.stack() try: the_class = stack[1][0].f_locals['self'].__class__ except(KeyError): the_class = None the_method = stack[1][0].f_code.co_name if the_class != cls: if methoddefiner(type(self), the_method) != cls: raise AttributeError("Cannot set readonly attribute '{}'".format(name)) return super(cls, self).__setattr__(name, value) def __delattr__(self, name): if name == '__dict__' or name in attrs: import inspect stack = inspect.stack() try: the_class = stack[1][0].f_locals['self'].__class__ except(KeyError): the_class = None the_method = stack[1][0].f_code.co_name if the_class != cls: if methoddefiner(type(self), the_method) != cls: raise AttributeError("Cannot delete readonly attribute '{}'".format(name)) return super(cls, self).__delattr__(name) clss = cls cls.__getattribute__ = __getattribute__ cls.__setattr__ = __setattr__ cls.__delattr__ = __delattr__ #This line will be moved when this algorithm will be compatible with inheritance cls = final(cls) return cls return classrebuilder def setreadonlyattributes(cls, *readonlyattrs): return readonlyattributes(*readonlyattrs)(cls) if __name__ == '__main__': #test readonlyattributes only as an indpendent module @readonlyattributes('readonlyfield') class ReadonlyFieldClass(object): def __init__(self, a, b): #Prevent initalization of the internal, unmodified PrivateFieldClass #External PrivateFieldClass can be initalized self.readonlyfield = a self.publicfield = b attr = None def main(): global attr pfi = ReadonlyFieldClass('forbidden', 'changable') ###---test publicfield, ensure its mutable---### try: #get publicfield print(pfi.publicfield) print('__getattribute__ works') #set publicfield pfi.publicfield = 'mutable' print('__setattr__ seems to work') #get previously set publicfield print(pfi.publicfield) print('__setattr__ definitely works') #delete publicfield del pfi.publicfield print('__delattr__ seems to work') #get publicfield which was supposed to be deleted therefore should raise AttributeError print(pfi.publlicfield) #publicfield wasn't deleted, raise RuntimeError raise RuntimeError('__delattr__ doesn\'t work') except(AttributeError): print('__delattr__ works') try: ###---test readonly, make sure its readonly---### #get readonlyfield print(pfi.readonlyfield) print('__getattribute__ works') #set readonlyfield, should raise AttributeError pfi.readonlyfield = 'readonly' #apparently readonlyfield was set, notify user raise RuntimeError('__setattr__ doesn\'t work') except(AttributeError): print('__setattr__ seems to work') try: #ensure readonlyfield wasn't set print(pfi.readonlyfield) print('__setattr__ works') #delete readonlyfield del pfi.readonlyfield #readonlyfield was deleted, raise RuntimeError raise RuntimeError('__delattr__ doesn\'t work') except(AttributeError): print('__delattr__ works') try: print("Dict testing") print(pfi.__dict__, type(pfi.__dict__)) attr = pfi.readonlyfield print(attr) print("__getattribute__ works") if pfi.readonlyfield != 'forbidden': print(pfi.readonlyfield) raise RuntimeError("__getattr__ doesn't work") try: pfi.__dict__ = {} raise RuntimeError("__setattr__ doesn't work") except(AttributeError): print("__setattr__ works") del pfi.__dict__ raise RuntimeError("__delattr__ doesn't work") except(AttributeError): print(pfi.__dict__) print("__delattr__ works") print("Basic things work") main()

There is no point to making read only attributes except when your writing library code, code which is being distributed to others as code to use in order to enhance their programs, not code for any other purpose, like app development. The __dict__ problem is solved, because the __dict__ is now of the immutable types.MappingProxyType, so attributes cannot be changed through the __dict__. Setting or deleting __dict__ is also blocked. The only way to change read only properties is through changing the methods of the class itself.

Though I believe my solution is better than of the previous two, it could be improved. These are this code's weaknesses:

a) Doesn't allow adding to a method in a subclass which sets or deletes a readonly attribute. A method defined in a subclass is automatically barred from accessing a readonly attribute, even by calling the superclass' version of the method.

b) The class' readonly methods can be changed to defeat the read only restrictions.

However, there is not way without editing the class to set or delete a read only attribute. This isn't dependent on naming conventions, which is good because Python isn't so consistent with naming conventions. This provides a way to make read only attributes that cannot be changed with hidden loopholes without editing the class itself. Simply list the attributes to be read only when calling the decorator as arguments and they will become read only.

Credit to Brice's answer in How to get the caller class name inside a function of another class in python? for getting the caller classes and methods.