isNumber? check

Tony Meyer ta-meyer at ihug.co.nz
Tue Sep 30 06:04:11 EDT 2003


[me]
> > """
> > def isInt(s):
> >     try:
> >         i = int(s)
> >     except ValueError:
> >         i = None
> >     return i
> > def isFloat(s):
> >     try:
> >         i = float(s)
> >     except ValueError:
> >         i = None
> >     return i
> > def isNumber(s):
> >     return isInt(s) or isFloat(s)
> > """
[Peter Otten]
> Made-up use-case:
> 
> s = "123"
> value = isNumber(s)
> if value is not None:
>     value *= 2
> else:
>     raise ValueError, "%s is not a number" % s

I would think that:
"""
s = "123"
value = isNumber(s)
if value is None:
    # do something that shows it's not a number.
    # (obviously not raise a ValueError, because
    # then there's no point using the function at
    # all)
# continue program, with value in value, if needed.
"""
Would be a more logical way to do it.

> To me this looks like a deliberate attempt to obfuscate. 

It's not.  It was an example case showing that there isn't any need to
bother with regexs and the like if you don't want to - Python will do the
work for you.  Assuming you trust the Python developers, you can trust that
it will correctly check that the value is convertible into a number, and
don't need to worry that you missed something in the regex (or whatever).

> Nobody would guess that you have to test the result of 
> isNumber() against None

A real function would have a docstring...this is only c.l.p...

> and cannot reliably treat it as a boolean value.

If returning a boolean value was a criteria, then you just replace "return
i" with "return True" or "return False".

> You should at least change the function name 
> to something like stringToNumber().

The OP asked for a function called isNumber.  I posted an example of one.

> The program flow is redundant: 
> - convert to number 
> - "convert" exception into special return value
> - check for special return value
> - throw exception

The program snippet doesn't throw an exception at the end; your example
usage did.  If the code was going to raise a ValueError if it couldn't be
converted, then it would be written differently; the OP didn't say that that
was the case.  The program flow is really:
 - convert to number and return
 - if an exception was raised then return None, signifying that no number
can be created

Lots of the stuff written about Python talks about using exceptions for code
control, as this does.  This isn't C++.

> I can think of very few situations where None could actually 
> be *used* instead of a numerical value.

Why would you want to *use* None?  It's simply the function signifying that
it couldn't convert the number.

> > Note that "0" will come back as 0.0, though, because it 
> > evaluates as False. It would be easy enough to special case that, if
necessary.
> 
> This is a bug that demonstrates that your isInt()/isFloat() 
> implementation is not even well suited to implement isNumber().

Rubbish.  This is me not taking care of every little detail in example code,
for the sake of brevity, but warning that that is the case.  In any case, it
still correctly determines if the object can be converted to a number, and
still correctly returns the number it is converted to.  The only thing that
is possibly incorrect is that it's a float instead of an int.  As I said,
easily fixed.

You seem to have missed the whole point of the code snippet.  The idea is
that you can use the conversion built into Python to check if a number can
be converted, without having to write code that checks yourself.  Perhaps
you would like this more?

"""
def isNumber(s):
    for f in [int, float,]:
        try:
            return f(s)
        except ValueError:
            pass
    raise ValueError("Cannot convert to a number")
"""

To me this doesn't seem as clear in explaining the concept, although it's
more like code I would actually use.  This does return "0" as 0, and raises
a ValueError is no conversion is possible.  The point is that the concept,
which is what I was trying to convey, is the same.  If you really don't want
the value returned, then you could just use:

"""
def isNumber(s):
    for f in [int, float,]:
        try:
            f(s)
        except ValueError:
            continue
        return True
    return False
"""

Probably not the fastest way to do it, but certainly simple.  (Although not
as simple as the hasattr(__int__) solution, which is very nice :)

=Tony Meyer






More information about the Python-list mailing list