2

I have a function that expects real numbers (either integers or floats) as its input, and I'm trying to validate this input before doing mathematical operations on it.

My first instinct is to cast inputs as floats from within a try-except block.

try:
   myinput = float(input)
except:
   raise ValueError("input is not a well-formed number")

I could also call isinstance(mydata, (float, int, long) ) but the list of "all these could be numbers" seems a bit inelegant to me.

What's the most pythonic way of going about it? Is there another option I overlooked?

Vijay Dev
  • 26,966
  • 21
  • 76
  • 96
Kena
  • 6,891
  • 5
  • 35
  • 46
  • Duplicate: http://stackoverflow.com/questions/354038/checking-if-string-is-a-number-python – S.Lott Dec 16 '08 at 14:28
  • Thanks! I don't know how I could have missed that. – Kena Dec 16 '08 at 14:33
  • Athough in my case, inputs are not strings, they're really floats or integers already. – Kena Dec 16 '08 at 14:34
  • @Kena: there's no distinction between numbers encoded as strings and numbers of the wrong type. It's all one. And the question's a dupe. ;-) – S.Lott Dec 16 '08 at 14:35
  • Python unfortunately disagrees, isinstance("1.0", (float)) is false – Kena Dec 16 '08 at 14:37
  • (Which might tilt the scale in favor of calling float instead of isinstance, I guess) – Kena Dec 16 '08 at 14:38
  • But I would not be asking myself this question if my inputs were strings. – Kena Dec 16 '08 at 14:47
  • Your try: ... except is very Pythonic, I find. – bortzmeyer Dec 16 '08 at 15:02
  • @Kena: " isinstance("1.0", (float))" has no bearing on what I said. What I said was float("1.0"), float(1), float(1+0j) and float(1L) are all one to Python. – S.Lott Dec 16 '08 at 15:10
  • But how can the question you linked answer my question, which is basically "am I better off calling float or isinstance", when isinstance would not have worked in that case since the OP was working with strings? – Kena Dec 16 '08 at 15:16
  • (You're still very welcome to answer my question with "use float(), and then your function would be able to handle strings just as well as numbers) – Kena Dec 16 '08 at 15:17

4 Answers4

12

To quote myself from How much input validation should I be doing on my python functions/methods?:

For calculations like sum, factorial etc, pythons built-in type checks will do fine. The calculations will end upp calling add, mul etc for the types, and if they break, they will throw the correct exception anyway. By enforcing your own checks, you may invalidate otherwise working input.

Thus, the best option is to leave the type checking up to Python. If the calculation fails, Python's type checking will give an exception, so if you do it yourself, you just duplicate code which means more work on your behalf.

Community
  • 1
  • 1
Egil
  • 5,600
  • 2
  • 32
  • 33
  • I agree with this. The point about scripting languages is to not have strongly typed code. I would add that assert statement should be used more for debugging. – Mapad Dec 16 '08 at 14:32
  • I agree; if you're just going to throw an exception anyway, then why not just say "x ** y" or whatever content in the knowledge that you'll get an exception if they're not numbers? – Eli Courtwright Dec 16 '08 at 14:33
  • But then, the type of my exception depends more or less on what I do, right? For instance, abs(input) would raise a TypeError, while float(input) would raise a ValueError. Should I bother about that? – Kena Dec 16 '08 at 14:36
  • No. If you bother and throw ValueError all the time, the differentiation by python would give more information on the error. – Egil Dec 16 '08 at 17:51
5

In Python 2.6 and 3.0, a type hierarchy of numeric abstract data types has been added, so you could perform your check as:

>>> import numbers
>>> isValid = isinstance(myinput , numbers.Real)

numbers.Real will match integral or float type, but not non-numeric types, or complex numbers (use numbers.Complex for that). It'll also match rational numbers , but presumably you'd want to include those as well. ie:

>>> [isinstance(x, numbers.Real) for x in [4, 4.5, "some string", 3+2j]]
[True, True, False, False]

Unfortunately, this is all in Python >=2.6, so won't be useful if you're developing for 2.5 or earlier.

Brian
  • 116,865
  • 28
  • 107
  • 112
2

Maybe you can use a combination of assert and isinstance statements. Something like the following is I think a more pythonic way, as you throw an exception whenever your inputs don't follow your requirements. Unfortunately I don't see any better definition of what is a valid number than yours. Maybe someone will come with a better idea.

number = (float, int, long)
assert isinstance(mydata, (float, int, long))
Mapad
  • 8,407
  • 5
  • 41
  • 40
1

I don't get the question.

There are two things with wildly different semantics tossed around as "alternatives".

A type conversion is one thing. It works with any object that supports __float__, which can be quite a variety of objects, few of which are actually numeric.

try:
   myinput = float(input)
except:
   raise ValueError("input is not a well-formed number")
# at this point, input may not be numeric at all
# it may, however, have produced a numeric value

A type test is another thing. This works only with objects that are proper instances of a specific set of classes.

isinstance(input, (float, int, long) )
# at this point, input is one of a known list of numeric types

Here's the example class that responds to float, but is still not numeric.

class MyStrangeThing( object ):
    def __init__( self, aString ):
        # Some fancy parsing 
    def __float__( self ):
        # extract some numeric value from my thing

The question "real numbers (either integers or floats)" is generally irrelevant. Many things are "numeric" and can be used in a numeric operation but aren't ints or floats. For example, you may have downloaded or created a rational numbers package.

There's no point in overvalidating inputs, unless you have an algorithm that will not work with some types. These are rare, but some calculations require integers, specifically so they can do integer division and remainder operations. For those, you might want to assert that your values are ints.

S.Lott
  • 384,516
  • 81
  • 508
  • 779