将字符串转换为浮点数,避免静默的NaN/Inf转换
我想在Python 2.6及以后的版本中把字符串转换成浮点数,但我希望在转换时不要默默地把像'NaN'
和'Inf'
这样的内容变成浮点对象。我希望这些无效的文本能被忽略,就像任何不符合浮点数表示的文本一样。
在2.6之前,float("NaN")
在Windows上会抛出一个ValueError错误。但现在它会返回一个浮点数,而用math.isnan()检查这个浮点数会返回True,这对我的应用来说并没有什么用。(正如有人指出的,这种行为一直依赖于平台,但对我来说,无论在哪里发生,这都是一种不希望出现的行为。)
目前我有的代码是:
import math
def get_floats(source):
for text in source.split():
try:
val = float(text)
if math.isnan(val) or math.isinf(val):
raise ValueError
yield val
except ValueError:
pass
这是一个生成器,我可以给它提供包含用空格分开的真实数字序列的字符串。我希望它只返回那些完全是浮点数表示的字段,比如"1.23"或"-34e6",而不是像"NaN"或"-Inf"这样的内容。完全不是浮点数的东西,比如"hello",也应该被忽略。
测试案例:
assert list(get_floats('1.23 foo -34e6 NaN -Inf')) == [1.23, -34000000.0]
请建议一些你认为更优雅的替代方案,即使它们涉及到“先看再跳”(这在Python中通常被认为是一种较低级的做法)。
编辑以澄清像"hello"这样的非浮点文本也应该被安静地忽略。目的是只提取出那些真实的数字,忽略其他所有内容。
4 个回答
0
我给Paul Hankin的回答点了赞,因为它看起来更容易理解。不过,如果我不想把代码拆得太细,这里有一个我最初代码的变种,显得不那么笨重。
def get_only_numbers(source):
'''yield all space-separated real numbers in source string'''
for text in source.split():
try:
val = float(text)
except ValueError:
pass # ignore non-numbers
else:
# "NaN", "Inf" get converted: explicit test to ignore them
if not math.isnan(val) and not math.isinf(val):
yield val
其实这和我最开始的代码差不多。
1
这是一个非常小的建议,不过使用 continue
这个命令比抛出一个错误要快一点:
def get_floats(source):
for text in source.split():
try:
val = float(text)
if math.isnan(val) or math.isinf(val): continue
yield val
except ValueError:
pass
使用 raise ValueError
的情况:
% python -mtimeit -s'import test' "list(test.get_floats('1.23 -34e6 NaN -Inf Hello'))"
10000 loops, best of 3: 22.3 usec per loop
使用 continue
的情况:
% python -mtimeit -s'import test' "list(test.get_floats_continue('1.23 -34e6 NaN -Inf Hello'))"
100000 loops, best of 3: 17.2 usec per loop
2
我会这样写。我觉得这样既简洁又容易读懂。
def is_finite(x):
return not math.isnan(x) and not math.isinf(x)
def get_floats(source):
for x in source.split():
try:
yield float(x)
except ValueError:
pass
def get_finite_floats(source):
return (x for x in get_floats(source) if is_finite(x))