How to pass objects as "constant" (immutable) in python.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | import operator
import types
import copy
class NoModifyError(RuntimeError): pass
class Constant:
# Standard Functions
def __init__(self, obj):
if type(obj) is types.InstanceType and issubclass(obj.__class__, Constant):
raise TypeError, "Cannot wrap constant in constant"
else:
self.__dict__["_value"] = obj
def __repr__(self):
return repr(self._value)
def __str__(self):
return str(self._value)
def __cmp__(self, x):
return cmp(self._value, x)
def __hash__(self):
return hash(self._value)
def __call__(self, *args):
return apply(self._value, args)
def __getattr__(self, x):
print repr(x)
print self.__dict__, self.__dict__.keys()
if self.__dict__.has_key(x):
return self.__dict__[x]
elif Constant.__dict__.has_key(x):
return Constant.__dict__[x]
else:
raise RuntimeError
return getattr(self._value, x)
def __setattr__(self, x, y):
raise NoModifyError, "Cannot set attribute on constant object."
def __delattr__(self, x):
raise NoModifyError, "Cannot delete attribute of constant object."
# List Functions
def __len__(self):
return len(self._value)
def __getitem__(self, x):
return self._value[x]
def __setitem__(self, x, y):
raise NoModifyError, "Cannot modify constant."
def __delitem__(self, x):
raise NoModifyError, "Cannot modify constant."
def __getslice__(self, x, y):
return self._value[x:y]
def __setslice__(self, x, y, z):
raise NoModifyError, "Cannot modify constant."
def __delslice__(self, x, y):
raise NoModifyError, "Cannot modify constant."
def __contains__(self, x):
return x in self._value
def __nonzero__(self):
if self._value: return 1
else: return 0
# Type Conversions
def __coerce__(self, other):
c = coerce(self._value, other)
return (copy.deepcopy(c[0]), c[1])
def __int__(self):
return int(self._value)
def __long__(self):
return long(self._value)
def __float__(self):
return float(self._value)
def __oct__(self):
return oct(self._value)
def __hex__(self):
return hex(self._value)
class DeepConstant(Constant):
def __getattr__(self, x):
return Constant(getattr(self._value))
def __getitem__(self, x):
return Constant(self._value[x])
def __getslice__(self, x, y):
return Constant(self._value[x:y])
|
Often times you want to pass an object that shouldn't be modified. This can be done by wrapping the object in the Constant class. The Constant class intercepts calls to __setattr__, __delattr__, __setitem__, __delitem__, __setslice__, and __delslice__ (all operators that can modify the object.)
Also, inplace operators are overloaded and intercepted.
DeepConstant is a recursive veresion of Constant that wraps all returned data.
The one place where this code fails is in the __coerce__ method. When __coerce__ is called, we make a copy of the coerced data. This way, we can never pass the wrapped object back to the caller.
Also, I have omitted the math operators (+,-,/,*,%,etc.) to make the example shorter.
recipe works well, except for DeepConstant which has a bug. __getattr__ should pass two arguments not one. The fix is as follows. Thanks, --Raj
DeepConstant is only skin deep...
will only give constancy to 1 more level. (e. g. DeepConstant([[["x"]]])[0][0][0]='y') The following should traverse the whole object structure:
apply()
is deprecated.apply(self._value, args)
should be changed toself._value(*args)