Welcome, guest | Sign In | My Account | Store | Cart

How to pass objects as "constant" (immutable) in python.

Python, 81 lines
 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.

3 comments

Raj Kunjithapadam 22 years, 2 months ago  # | flag

recipe works well, except for DeepConstant which has a bug. __getattr__ should pass two arguments not one. The fix is as follows. Thanks, --Raj

def __getattr__(self, x):
    return Constant(getattr(self._value, x))
Andreas Kloss 19 years, 7 months ago  # | flag

DeepConstant is only skin deep...

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])

will only give constancy to 1 more level. (e. g. DeepConstant([[["x"]]])[0][0][0]='y') The following should traverse the whole object structure:

class DeepConstant(Constant):
  def __getattr__(self, x):
    return DeepConstant(getattr(self._value))
  def __getitem__(self, x):
    return DeepConstant(self._value[x])
  def __getslice__(self, x, y):
    return DeepConstant(self._value[x:y])
a 14 years ago  # | flag

apply() is deprecated. apply(self._value, args) should be changed to self._value(*args)