int/long unification hides bugs
Andrew Dalke
adalke at mindspring.com
Wed Oct 27 02:45:16 EDT 2004
Bengt Richter wrote:
> If you are willing to make your variables exist in an object's attribute
> name space, you can define almost any behavior you want.
Or, here's another solution -- make a number-like object which
handles range checking for every operation.
The big problem here is that unlike Ada, C++, or other languages
that let you declare variable types, I have to figure out how
to merge the allowed ranges of the two values during a binary op.
I decided to use the intersection. Unlike Pythons ranges, I
choose low <= val <= high (as compared to low <= val < high).
import sys
class RangedNumber:
def __init__(self, val, low = -sys.maxint-1, high = sys.maxint):
if not (low <= high):
raise ValueError("low(= %r) > high(= %r)" % (low, high))
if not (low <= val <= high):
raise ValueError("value %r not in range %r to %r" %
(val, low, high))
self.val = val
self.low = low
self.high = high
def __str__(self):
return str(self.val)
def __repr__(self):
return "RangedNumber(%r, %r, %r)" % (self.val, self.low, self.high)
def __int__(self):
return self.val
def __float__(self):
return self.val
def _get_range(self, other):
if isinstance(other, RangedNumber):
low = max(self.low, other.low)
high = min(self.high, other.high)
other_val = other.val
else:
low = self.low
high = self.high
other_val = other
return other_val, low, high
def __add__(self, other):
other_val, low, high = self._get_range(other)
x = self.val + other_val
return RangedNumber(x, low, high)
def __radd__(self, other):
other_val, low, high = self._get_range(other)
x = other_val + self.val
return RangedNumber(x, low, high)
def __sub__(self, other):
other_val, low, high = self._get_range(other)
x = self.val - other_val
return RangedNumber(x, low, high)
def __rsub__(self, other):
other_val, low, high = self._get_range(other)
x = other_val - self.val
return RangedNumber(x, low, high)
def __abs__(self):
return RangedNumber(abs(self.val), self.low, self.high)
def __mul__(self, other):
other_val, low, high = self._get_range(other)
x = self.val * other_val
return RangedNumber(x, low, high)
def __rmul__(self, other):
other_val, low, high = self._get_range(other)
x = other_val * self.val
return RangedNumber(x, low, high)
# ... and many, many more ...
Here's some code using it
>>> a = RangedNumber(10, 0, 100)
>>>
>>> a
RangedNumber(10, 0, 100)
>>> print a
10
>>> a+90
RangedNumber(100, 0, 100)
>>> a+91
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "spam.py", line 39, in __add__
return RangedNumber(x, low, high)
File "spam.py", line 8, in __init__
raise ValueError("value %r not in range %r to %r" %
ValueError: value 101 not in range 0 to 100
>>> a*5
RangedNumber(50, 0, 100)
>>> 10*a
RangedNumber(100, 0, 100)
>>> 11*a
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "spam.py", line 67, in __rmul__
return RangedNumber(x, low, high)
File "spam.py", line 8, in __init__
raise ValueError("value %r not in range %r to %r" %
ValueError: value 110 not in range 0 to 100
>>> 0-a
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "spam.py", line 54, in __rsub__
return RangedNumber(x, low, high)
File "spam.py", line 8, in __init__
raise ValueError("value %r not in range %r to %r" %
ValueError: value -10 not in range 0 to 100
>>> a-0
RangedNumber(10, 0, 100)
>>> b = RangedNumber(18, 5, 20)
>>> b = RangedNumber(18, 5, 30)
>>> a+b
RangedNumber(28, 5, 30)
>>>
Andrew
dalke at dalkescientific.com
More information about the Python-list
mailing list