有没有办法告诉一个字符串是否代表一个整数(例如,'3','-17'但不是'3.14'或'asfasfas')而不使用try/except机制?
is_int('3.14') == False
is_int('-7') == True
有没有办法告诉一个字符串是否代表一个整数(例如,'3','-17'但不是'3.14'或'asfasfas')而不使用try/except机制?
is_int('3.14') == False
is_int('-7') == True
当前回答
先决条件:
我们谈论的是整数(不是小数/浮点数); 内置int()的行为是我们的标准(有时很奇怪:“-00”是它的正确输入)
简短的回答:
使用下面的代码。它简单,正确(虽然这个线程中的许多变体不是),并且几乎是try/except和regex变体的两倍。
def is_int_str(string):
return (
string.startswith(('-', '+')) and string[1:].isdigit()
) or string.isdigit()
TL;博士答:
我已经测试了3个主要变体(1)try/except, (2) re.match()和(3)字符串操作(见上文)。第三个变体比try/except和re.match()快两倍。顺便说一句:regex变体是最慢的!请参见下面的测试脚本。
import re
import time
def test(func, test_suite):
for test_case in test_suite:
actual_result = func(*test_case[0])
expected_result = test_case[1]
assert (
actual_result == expected_result
), f'Expected: {expected_result} but actual: {actual_result}'
def perf(func, test_suite):
start = time.time()
for _ in range(0, 1_000_000):
test(func, test_suite)
return time.time() - start
def is_int_str_1(string):
try:
int(string)
return True
except ValueError:
return False
def is_int_str_2(string):
return re.match(r'^[\-+]?\d+$', string) is not None
def is_int_str_3(string):
return (
string.startswith(('-', '+')) and string[1:].isdigit()
) or string.isdigit()
# Behavior of built-in int() function is a standard for the following tests
test_suite = [
[['1'], True], # func('1') -> True
[['-1'], True],
[['+1'], True],
[['--1'], False],
[['++1'], False],
[['001'], True], # because int() can read it
[['-00'], True], # because of quite strange behavior of int()
[['-'], False],
[['abracadabra'], False],
[['57938759283475928347592347598357098458405834957984755200000000'], True],
]
time_span_1 = perf(is_int_str_1, test_suite)
time_span_2 = perf(is_int_str_2, test_suite)
time_span_3 = perf(is_int_str_3, test_suite)
print(f'{is_int_str_1.__name__}: {time_span_1} seconds')
print(f'{is_int_str_2.__name__}: {time_span_2} seconds')
print(f'{is_int_str_3.__name__}: {time_span_3} seconds')
输出是:
is_int_str_1: 4.314162969589233 seconds
is_int_str_2: 5.7216269969940186 seconds
is_int_str_3: 2.5828163623809814 seconds
其他回答
如果你真的不喜欢到处使用try/except,请写一个helper函数:
def represents_int(s):
try:
int(s)
except ValueError:
return False
else:
return True
>>> print(represents_int("+123"))
True
>>> print(represents_int("10.0"))
False
它将需要更多的代码来精确覆盖Python认为是整数的所有字符串。要我说,你就用蟒语吧。
我认为
s.startswith('-') and s[1:].isdigit()
最好重写为:
s.replace('-', '').isdigit()
因为s[1:]也创建了一个新的字符串
但更好的解决办法是
s.lstrip('+-').isdigit()
据我所知,你想检查字符串可转换的int。要做到这一点你可以:
将'-'替换为空,因为'-'不是数字和'-7'也可以转换为int。 检查一下是不是数字。
def is_string_convertable_to_int(value: str) -> bool:
return value.replace('-', '').isdigit()
另外,你可以很容易地修改这个def来检查字符串在float中的可转换性,只需添加replace('。', "),并检查一个'。'使用value.count('.') = 1存在。
正确的RegEx解决方案应该结合Greg Hewgill和Nowell的思想,但不使用全局变量。可以通过将属性附加到方法来实现这一点。另外,我知道在方法中导入是不受欢迎的,但我想要的是像http://peak.telecommunity.com/DevCenter/Importing#lazy-imports这样的“惰性模块”效果
edit:到目前为止,我最喜欢的技术是使用String对象的独占方法。
#!/usr/bin/env python
# Uses exclusively methods of the String object
def isInteger(i):
i = str(i)
return i=='0' or (i if i.find('..') > -1 else i.lstrip('-+').rstrip('0').rstrip('.')).isdigit()
# Uses re module for regex
def isIntegre(i):
import re
if not hasattr(isIntegre, '_re'):
print("I compile only once. Remove this line when you are confident in that.")
isIntegre._re = re.compile(r"[-+]?\d+(\.0*)?$")
return isIntegre._re.match(str(i)) is not None
# When executed directly run Unit Tests
if __name__ == '__main__':
for obj in [
# integers
0, 1, -1, 1.0, -1.0,
'0', '0.','0.0', '1', '-1', '+1', '1.0', '-1.0', '+1.0',
# non-integers
1.1, -1.1, '1.1', '-1.1', '+1.1',
'1.1.1', '1.1.0', '1.0.1', '1.0.0',
'1.0.', '1..0', '1..',
'0.0.', '0..0', '0..',
'one', object(), (1,2,3), [1,2,3], {'one':'two'}
]:
# Notice the integre uses 're' (intended to be humorous)
integer = ('an integer' if isInteger(obj) else 'NOT an integer')
integre = ('an integre' if isIntegre(obj) else 'NOT an integre')
# Make strings look like strings in the output
if isinstance(obj, str):
obj = ("'%s'" % (obj,))
print("%30s is %14s is %14s" % (obj, integer, integre))
对于那些不太喜欢冒险的同学,输出如下:
I compile only once. Remove this line when you are confident in that.
0 is an integer is an integre
1 is an integer is an integre
-1 is an integer is an integre
1.0 is an integer is an integre
-1.0 is an integer is an integre
'0' is an integer is an integre
'0.' is an integer is an integre
'0.0' is an integer is an integre
'1' is an integer is an integre
'-1' is an integer is an integre
'+1' is an integer is an integre
'1.0' is an integer is an integre
'-1.0' is an integer is an integre
'+1.0' is an integer is an integre
1.1 is NOT an integer is NOT an integre
-1.1 is NOT an integer is NOT an integre
'1.1' is NOT an integer is NOT an integre
'-1.1' is NOT an integer is NOT an integre
'+1.1' is NOT an integer is NOT an integre
'1.1.1' is NOT an integer is NOT an integre
'1.1.0' is NOT an integer is NOT an integre
'1.0.1' is NOT an integer is NOT an integre
'1.0.0' is NOT an integer is NOT an integre
'1.0.' is NOT an integer is NOT an integre
'1..0' is NOT an integer is NOT an integre
'1..' is NOT an integer is NOT an integre
'0.0.' is NOT an integer is NOT an integre
'0..0' is NOT an integer is NOT an integre
'0..' is NOT an integer is NOT an integre
'one' is NOT an integer is NOT an integre
<object object at 0x103b7d0a0> is NOT an integer is NOT an integre
(1, 2, 3) is NOT an integer is NOT an integre
[1, 2, 3] is NOT an integer is NOT an integre
{'one': 'two'} is NOT an integer is NOT an integre
Greg Hewgill的方法缺少了几个组件:前导的“^”只匹配字符串的开头,并且预先编译re。但是这种方法可以让你避免尝试:
import re
INT_RE = re.compile(r"^[-]?\d+$")
def RepresentsInt(s):
return INT_RE.match(str(s)) is not None
我很感兴趣为什么你试图避免尝试:除了?