How can I tell when a string is in fact a number?

Alex Martelli aleaxit at yahoo.com
Wed Nov 8 03:55:27 EST 2000


"Thomas Gagne" <tgagne at ix.netcom.com> wrote in message
news:3A08D246.7FF1D355 at ix.netcom.com...
> OK.  You have a point.  Ignore what it returns for now, but I think having
a
> method #isNumber would be useful, and very self-documenting.

You mean, a method '#isNumber' of string objects as opposed to the
existing method 'isdigit' of the same objects (which we had all
forgotten about when discussing this...:-)?

Well, maybe, but it's a tricky question to determine what strings should
meet the 'is a number' condition!  To wit, consider such strings as...:
    1234
    1234123412341234
    1234123412341234L
    7,234,918
    7,234,918.00
    +77
    -41
    3.1415926
    23j
    23e2
    092
    0XFEED
    1/2
which of these should work, in your opinion...?  (Python's own
opinion may differ!-)

I suspect that 'being a number' may be too vague a concept for
most applications.  I dread the task of explaining to a beginner
why 0j 'is a number' while 0k isn't, why 1e1 'is a number' while
0e0 or 1f1 aren't, why 3+2j 'is a number' while 3+2 or 3/2 aren't,
and ditto for a 'too-long' sequence of digits to fit in an int,
a digit-sequence with a leading 0 digit and a 9 somewhere, ...
(deeming that a complex, float, and/or long, is not "a number",
is fraught with just as many difficulties).

We _might_ have "just-checking" versions of every specific built-in
"factory-function", I guess.  For example, since
    atoi('092')
works fine (and return 92) while
    atoi('092',0)
throws a ValueError, we might systematically have a
    wouldsucceedifyoutriedit
higher-order function...:
    wouldsucceedifyoutriedit(atoi, '092')       # returns 1
    wouldsucceedifyoutriedit(atoi, '092', 0)    # returns 0


It's actually a rather simple Python exercise to write this
higher-order function TODAY, e.g....:

def wouldsucceedifyoutriedit(function, *args):
    try: return apply(function, args) or 1
    except ValueError: return 0


Would it give significant added value to have it "built-in"?
I'm not sure, but I suspect it wouldn't.  It would, I guess,
encourage the traditional "look before you leap" approach...:

from string import atoi

def makeanumberwithadefault(str, default, function=atoi, *otherargs):
    if wouldsucceedifyoutriedit(function, *(str,)+otherargs):
        return function(str, *otherargs)
    else:
        return default

rather than the more Pythonish "it's better to ask forgiveness
than permission" idiom...:

def makeanumberwithadefault(str, default, function=atoi, *otherargs):
    try: return function(str, *otherargs)
    except ValueError: return default

I think the latter approach is what should be encouraged
instead -- it avoids duplication of operations, has more
general applicability, is actually simpler (once one gets
past the 'mental block' about deliberately trying some
operation that has good likelihood of failing...).


It _is_, of course, a matter of degrees -- we do, after
all, have "just-checking" versions of several operations
that _might_ be handled with appropriate try/except blocks
and/or default-values (hasattr, dict.has_key, ...).  But
here, since there is no single generic 'make this string
into a number of whatever numerical type it happens to
fit best', we'd have to keep genericity in terms of a
function-argument (and other potential arguments, see e.g.
the string.atoi case, where the second, optional argument
is an important indicator -- it gives the number-base...).

This nudges up the conceptual complexity of the "just
checking" case, and pushes down the performance of the
"look before you leap" approach; IMHO, the combined
effect is amply sufficient to suggest that, for this
case, we should encourage the more general and simpler
"it's better to ask forgiveness than permission" idiom.


Alex






More information about the Python-list mailing list