[Tutor] class uncertainty

Hans Nowak hnowak@cuci.nl
Tue, 28 Aug 2001 09:47:18 +0200


>===== Original Message From "Christopher Smith" <csmith@blakeschool.org> 
=====
>I've created a class which keeps track of the uncertainty in a number and
>subsequent calculations performed with that number.  It works like this:
>
>u=uncertainty #shorten the name
>
># define physical measurements
># with +/-1 in last digit by default
>p=u('1.0')
>R=u('.0821')
>T=u('298')
>
># define a physical measurement witha larger uncertainty
>n=u('1.0,.15')
>
>#do the calculation and look at results
>v=n*R*T/p
>print v
>print v[0]
>print v[1]
>
>#results are
># 24 +/- 9
># 24.4658
># 9.31980083228

Looks neat... why is u() taking strings, though?

>I am in need of some advice now regarding two things:
>
>1)  I want to be able to use the math functions on the class.
>Should I do this as methods, e.g. x.log(), or should I redefine
>the math functions so that when the module is loaded you now have
>
>def log(x):
>	if type(x)==<the type of my class>:
>		return uncertain.log(x)
>	return math.log(x)
>
>I would prefer the latter so the operation is as seamless as possible.
>That's where I need the advise, though.

Redefining existing functions is usually a bad idea, but in this case you're 
preserving the original behavior of math.log for objects other than your 
class, so I guess it's not as bad. This is more of a design question than 
anything else... will the users of your class expect methods or functions? 
This may be simplified to: are they coming from an OO background, or a 
functional programming one? I don't think there's much to say to give one a 
significant benefit over the other, except maybe that <your_class>.log() would 
be less ambiguous than using a log function.

There's another possibility, though... I don't know if this makes sense for 
your class, but you can define the __float__ method, and have it return the 
float value you're trying to take the log() of. A trivial example:

>>> class Foo:
	def __init__(self, x):
		self.x = x
	def __float__(self):
		try:
			return float(self.x)
		except:
			return 1.0


>>> f = Foo(99)
>>> math.log(f)
4.5951198501345898
>>> g = Foo("bla")
>>> math.log(g)
0.0

I haven't tested this thoroughly, but I think it might be a solution for your 
problem.

>2)  Along the same lines, I would prefer not to have to explicitly create
>my class.  Is there a way to overload the "=" like the algebraic operators?

Nope. :)

>Once you load the class it would treat all numbers as strings and make them
>into class instances unless, for example, they were followed by 'x' which
>would denote 'exact'.  My guess this is not the way to go.
>
>Would another approach be to write an evaluator which would work like this:
>
>####
>#put the whole 'calculation in triple quotes
>res=uevaluate('''
>p=1.0
>R=0.0821
>T=298.15
>n=1.0,.15
>
>v=n*R*T/p
>print v
>
>in=3.0
>print in*2.54x #the x denotes an exact value
>'''
># and now see the results
>for ri in res:
>	print ri
>
>24 +/- 9
>7.6 +/- 0.3
>####

Ouch... this last approach would require you to parse the code, find all (?) 
numbers, and replace them with u() instances. I think your original approach 
was just fine... at least people reading the code would know you are dealing 
with uncertainty objects. This is better than implicit conversions, IMHO.

HTH,

--Hans Nowak