Problem 📖
In Python you can not create a true read-only attribute, but you can make modifying it harder.
You can make modifying an attribute a true headache even with Python. Here is the recipe. Even if you were willing to go to C-extension level, modifications to attributes could still be done but they would require using C pointers.
Solution 🍝
from types import MappingProxyType as Proxy
class Person:
"""A person with two read-only attributes.
Setting __slots__ will prevent adding new attributes, but __slots__ itself
can still be modified. Custom __setattr__ will prevent direct modification
of __slots__ while custom __getattr__ will direct attribute lookup towards
the real keys hiding behind the proxy attribute. Then, MappingProxyType is
used to prevent easily modifying the proxy attribute even when accessed by
using object.__getattribute__ and object.__setattr__ to bypass the methods
defined in 'read-only' class. Normal 'self.proxy' can not be used here due
to the __getattr__ and __setattr__ preventing it.
"""
__slots__ = ("proxy")
def __init__(self, name, ssn):
object.__setattr__(self, "proxy", Proxy({"name": name, "ssn": ssn}))
def __getattr__(self, name):
proxy = object.__getattribute__(self, "proxy")
if (name == "proxy") or (name not in proxy):
return object.__getattribute__(self, name)
return proxy[name]
def __setattr__(self, _name, _val):
raise AttributeError("Read-only object")
Example 🖥️
>>> person = Person("Joe", 123)
>>>
>>> person.name, person.ssn
('Joe', 123)
>>>
>>> person.name, person.ssn = "Niinistö", 1337
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 22, in __setattr__
AttributeError: Read-only object
>>>
>>>
>>> person.fake_attribute = 1337
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 22, in __setattr__
AttributeError: Read-only object
>>>
__init__
also calls__setattr__
– Aileen