|
|
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: