I have a mutable class in Python which I would like to be able to "freeze", at that point its immutable, therefor can have a __hash__ function.
My concern is, will having the __hash__ function present will make Python behave strangely because it may check for the existence of a hash function.
I realize I could use a subclass that has a hash function, copy the class to a subtype. But I'm interested to know if having an optional hash function is supported by Python.
In the example below it works in basic cases (but may fail in others).
Note: This assumes you don't touch _var or _is_frozen directly and only use access methods.
Note: its probably more Pythonic not to use this method and instead have a FrozenMyVar class, but Im curious if this can be considered to be supported in Python or not.
class MyVar:
__slots__ = ("_var", "_is_frozen")
def __init__(self, var):
self._var = var
self._is_frozen = False
def freeze(self):
self._is_frozen = True
def __hash__(self):
if not self._is_frozen:
raise TypeError("%r not hashable (freeze first)" % type(self))
return hash(self._var)
def __eq__(self, other):
try:
return self.val == other.val
except:
return NotImplemented
@property
def var(self):
return self._var
@var.setter
def var(self, value):
if self._is_frozen:
raise AttributeError("%r is frozen" % type(self))
self._var = value
# ------------
# Verify Usage
v = MyVar(10)
v.var = 9
try:
hash(v)
except:
print("Hash fails on un-frozen instance")
v.freeze()
try:
v.var = 11
except:
print("Assignment fails on frozen instance")
print("Hash is", hash(v))
Adding a note on the real-world use-case, We have some linear math module with Vector/Matrix/Quaternion/Euler classes. In some cases we want to have for eg, a "set of matrices" or a "dict with vector keys". Its always possible to expand them into tuples but they take up more memory & loose their abilities to behave a our own math types - so the ability to freeze them is attractive.