You could make the global en immutable data structure, like this:
class Person:
"""Immutable person class"""
# Using __slots__ reduces memory usage.
# If __slots__ doesn't include __dict__, new attributes cannot be added.
# This is not always desirable, e.g. it you want to subclass Person.
__slots__ = ("name", "age")
def __init__(self, name, age):
"""Create a Person instance.
Arguments:
name (str): Name of the person.
age: Age of the person. Must be convertible to a float.
"""
# Parameter validation. This shows how to do this,
# but you don't always want to be this inflexibe.
if not isinstance(name, str):
raise ValueError("'name' must be a string")
# Use super to get around __setattr__ definition
super(Person, self).__setattr__("name", name)
try:
# A little more flexible parameter validation.
super(Person, self).__setattr__("age", round(float(age)))
except ValueError:
raise ValueError("'age' cannot be converted to int")
def __setattr__(self, name, value):
"""Prevent modification of attributes."""
raise AttributeError(f"{type(self)} cannot be modified")
def __repr__(self):
"""Create a string representation of the Person.
You should always have at least __repr__ or __str__
for interactive use.
"""
return f'<Person(name="{self.name}", age={self.age})>'
You can create instances of this type:
In [2]: PERSON = Person("John Doe", 37)
Out[2]: <Person(name="John Doe", age=37)>
But you cannot easily modify them:
In [3]: PERSON.name = "Foo Bar"
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-3-139d70f50d64> in <cell line: 1>()
----> 1 PERSON.name = "Foo Bar"
<ipython-input-1-41ab3e15b161> in __setattr__(self, name, value)
28 def __setattr__(self, name, value):
29 """Prevent modification of attributes."""
---> 30 raise AttributeError(f"{type(self)} cannot be modified")
31
32 def __repr__(self):
AttributeError: <class '__main__.Person'> cannot be modified
(You can actually modify them using super, as shown in Person.__init__(). But this at least protects you from casual mistakes.)
This of course cannot prevent you from assigning another object to PERSON!
That is not possible in Python.
And it would basically require a modification of the language to make that possible.
There is the convention that variables named in ALLCAPS are constants, but that's just a convention. To the best of my knowledge, this cannot be enforced.
Edit1
Python has existed for decades with the convention that ALL_CAPS variables are constants. Apparently this has worked well enough that a change in the language to include say a const statement has not been deemed necessary.