Representing uncertainty in Python

[posted by Gavin Robinson, 11:50 am, 28 February 2008]

In December I wrote some Python code to do calculations with pre-decimal British currency. As well as dealing with the awkwardness of pounds, shillings, and pence, I needed to allow for situations where a damaged or illegible manuscript made the values uncertain. To start with I wrote a class called MetaOldMoney which could store exact amounts of money or ranges of values.

Now I’ve written some new code which can easily deal with uncertain values of anything. There are three classes: one to represent an exact value, one to represent a range where the minimum and maximum values are known, and one to represent a minimum value with no maximum. Instances of all three objects contain a tuple of two values. For an exact value they’re both the same, for a range they contain the upper and lower amount, and for a minimum the second value is set to None. The addition operator is redefined so that any combination of these objects can be added together, returning an object of the correct type eg Exact + Range = Range etc. The best thing is that the values contained can be of absolutely any type. Taking full advantage of the Python approach to typing, the classes I’ve defined don’t even need to know what they contain. The addition will just work as long as the contained objects can be added together.

These classes are so flexible that there are lots of different ways I could use them. I could put my OldMoney objects inside them, or I could define a new money object which contains individual uncertain values for pound, shillings and pence. I could even nest the objects inside each other to allow for situations where the maximum value in a range is also a range.

Code below:

class MetaExact:

    def __init__(self, value):
        self.value = value, value

    def __add__(self, other):
        if other.value[1]:
            if other.value[0] == other.value[1]:
                return MetaExact(self.value[0] + other.value[0])
            else:
                return MetaRange(self.value[0] + other.value[0],
self.value[0] + other.value[1])
        else:
            return MetaMin(self.value[0] + other.value[0])

class MetaRange:

    def __init__(self, lower, upper):
        self.value = lower, upper

    def __add__(self, other):
        if other.value[1]:
            return MetaRange(self.value[0] + other.value[0],
self.value[1] + other.value[1])
        else:
            return MetaMin(self.value[0] + other.value[0])

class MetaMin:
    def __init__(self, value):
        self.value = value, None

    def __add__(self, other):
        return MetaMin(self.value[0] + other.value[0])

def sumMeta(values):
    total = MetaExact(values[0].value - values[0].value)
    for x in values:
        total += x
    return total

The sumMeta function works like the built-in sum function but with Meta objects. Again it doesn’t need to know what kind of objects are contained in the value tuples of the Meta objects. The total variable is initialized with a MetaExact object containing objects of the correct type but with 0 value by subtracting the first value in the sequence from itself.

3 Comments »

RSS feed for comments on this post.

TrackBack URI

Leave a comment

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

If your comment does not appear, it has been held for moderation. Please do not submit it again.

If you supply a false e-mail address your comment will be deleted.