tl;dr: Don't pass a live object; pass a dumbed-down representation, and pass it for reference only (do not accept it back) if you want any security against tampering.
You can protect an attribute from modification with a class-private attribute and a property:
class Student(object): __counter = 0 def __init__(self): self.__class__.__counter += 1 # Only works within the class. self.__ordinal = self.__counter @property def ordinal(self): return self.__ordinal
It works as expected, and does not allow to easily tamper with itself. Tampering attempts look puzzling and misleading to those who don't know how private attributes work.
How it works:
>>> s1 = Student() >>> s1.ordinal 1 >>> s2 = Student() >>> s2.ordinal 2 >>> s2.ordinal = 88 Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: can't set attribute >>> s2.__ordinal = 88 # A cunning student. >>> s2.__ordinal # Gasp! 88 >>> s2.ordinal # Nope. The private attribute is not touched. 2 >>> Student.__counter # Trying to override the true source. Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: type object 'Student' has no attribute '__counter' >>> Student.__counter = 123 # This appears to succeed! >>> s3 = Student() >>> s3.ordinal # Again, the private attribute is not touched. 3 >>> _
Despite the above, this is not bulletproof. With enough determination, the private class attribute can be accessed:
>>> Student._Student__counter = 999 >>> s1000 = Student() >>> s1000.ordinal 1000
Same applies to any hidden-attribute answers (a number is given); as long as __dict__ is visible, the hidden attribute is not exactly hidden.
Much more sophisticated defenses can be built around attribute access, including inspecting the stack to determine the caller. But as long as you pass a Python object that has any access to the master state, you have a security hole, and a determined attacker will be able to alter that master state.
For real tamper-proof access, when you pass data to a non-trusted party:
- Only pass
Student objects as dumb stores of attributes, and functions computing something from these attributes (not mutating them). - Keep your state in a database, and never pass any references to that database in your
Student objects. - Only accept the student ID, or some other DB-related identifier, in any API calls that modify the master state.
- Always look up that state from the database when updating it.
- Authenticate the callers of your API, so that they only can work with student IDs they supposed to.
This may be or be not an overkill in your particular situation; you did not describe it.
__dict__(where the property will make it effectively invisible), and override__new__very carefully.(1).to_bytes(7,'little'))