Python: Identifying a numeric string?
I tried a couple of approaches, I am really only concerned with performance, not correctness. I noticed that the regex based im开发者_如何学JAVAplementation is about 3-4x slower than the one that uses type coercion. Is there another, more efficient way of doing this?
def IsNumber(x):
try:
_ = float(x)
except ValueError:
return False
return True
def IsNumber2(x):
import re
if re.match("^\d*.?\d*$", x) == None:
return False
return True
Thanks!
First of all, they're not doing the same thing. Floats can be specified as "1e3", for example, and float() will accept that. It's also not coercion, but conversion.
Secondly, don't import re in IsNumber2, especially if you're trying to use it with timeit. Do the import outside of the function.
Finally, it doesn't surprise me that float() is faster. It's a dedicated routine written in C for a very specific purpose, while regex must be converted into a form that's interpreted.
Is your first version, that uses float(), fast enough? It should be, and I don't know of a better way to do the same thing in Python.
Not really. Coercion is the accepted way to do this.
The answer depends a lot on what you mean by 'numeric string'. If your definition of numeric string is 'anything that float accepts', then it's difficult to improve on the try-except method.
But bear in mind that float may be more liberal than you want it to be: on most machines, it'll accept strings representing infinities and nans. On my machine, it accepts 'nan(dead!$#parrot)'
, for example. It will also accept leading and trailing whitespace. And depending on your application, you may want to exclude exponential representations of floats. In these cases, using a regex would make sense. To just exclude infinities and nans, it might be quicker to use the try-except method and then use math.isnan and math.isinf to check the result of the conversion.
Writing a correct regex for numeric strings is a surprisingly error-prone task. Your IsNumber2
function accepts the string '.'
, for example. You can find a battle-tested version of a numeric-string regex in the decimal module source. Here it is (with some minor edits):
_parser = re.compile(r""" # A numeric string consists of:
(?P<sign>[-+])? # an optional sign, followed by either...
(
(?=\d|\.\d) # ...a number (with at least one digit)
(?P<int>\d*) # having a (possibly empty) integer part
(\.(?P<frac>\d*))? # followed by an optional fractional part
(E(?P<exp>[-+]?\d+))? # followed by an optional exponent, or...
|
Inf(inity)? # ...an infinity, or...
|
(?P<signal>s)? # ...an (optionally signaling)
NaN # NaN
(?P<diag>\d*) # with (possibly empty) diagnostic info.
)
\Z
""", re.VERBOSE | re.IGNORECASE | re.UNICODE).match
This pretty much matches exactly what float accepts, except for the leading and trailing whitespace and some slight differences for nans (the extra 's' for signaling nans, and the diagnostic info). When I need a numeric regex, I usually start with this one and edit out the bits I don't need.
N.B. It's conceivable that float could be slower than a regex, since it not only has to parse the string, but also turn it into a float, which is quite an involved computation; it would still be a surprise if it were, though.
You might try compiling your regular expression first, but I'd imagine it would still be slower.
Also, if you want to know if your string is a number because you're going to do calculations with it, you'll have to coerce it anyway.
精彩评论