Có cách nào để biết liệu một chuỗi đại diện cho một số nguyên (ví dụ '3'
, '-17'
nhưng không '3.14'
hoặc 'asfasfas'
) mà không sử dụng cơ chế thử / ngoại trừ?
is_int('3.14') = False
is_int('-7') = True
Có cách nào để biết liệu một chuỗi đại diện cho một số nguyên (ví dụ '3'
, '-17'
nhưng không '3.14'
hoặc 'asfasfas'
) mà không sử dụng cơ chế thử / ngoại trừ?
is_int('3.14') = False
is_int('-7') = True
Câu trả lời:
Nếu bạn thực sự khó chịu khi sử dụng try/except
s ở mọi nơi, vui lòng chỉ viết một hàm trợ giúp:
def RepresentsInt(s):
try:
int(s)
return True
except ValueError:
return False
>>> print RepresentsInt("+123")
True
>>> print RepresentsInt("10.0")
False
Sẽ là CÁCH nhiều mã hơn để bao gồm chính xác tất cả các chuỗi mà Python coi là số nguyên. Tôi nói chỉ là pythonic về điều này.
>>> print RepresentsInt(10.0)
True
>>> print RepresentsInt(10.06)
True
với số nguyên dương bạn có thể sử dụng .isdigit
:
>>> '16'.isdigit()
True
mặc dù nó không hoạt động với số nguyên âm. giả sử bạn có thể thử như sau:
>>> s = '-17'
>>> s.startswith('-') and s[1:].isdigit()
True
nó sẽ không hoạt động với '16.0'
định dạng, tương tự như int
đúc theo nghĩa này.
chỉnh sửa :
def check_int(s):
if s[0] in ('-', '+'):
return s[1:].isdigit()
return s.isdigit()
u'²'.isdigit()
là đúng nhưng int(u'²')
làm tăng ValueError. Sử dụng u.isdecimal()
thay thế. str.isdigit()
là phụ thuộc vào miền địa phương trên Python 2.
check_int('')
sẽ đưa ra một ngoại lệ thay vì quay trở lạiFalse
Bạn biết đấy, tôi đã tìm thấy (và tôi đã thử nghiệm điều này nhiều lần) rằng thử / ngoại trừ không thực hiện tốt tất cả, vì bất kỳ lý do gì. Tôi thường xuyên thử một số cách để làm mọi thứ và tôi không nghĩ rằng mình đã từng tìm thấy một phương pháp sử dụng thử / ngoại trừ để thực hiện tốt nhất những thử nghiệm đó, thực tế thì dường như những phương pháp đó thường xuất hiện gần với tồi tệ nhất, nếu không phải là tồi tệ nhất. Không phải trong mọi trường hợp, nhưng trong nhiều trường hợp. Tôi biết nhiều người nói đó là cách "Pythonic", nhưng đó là một lĩnh vực mà tôi chia tay với họ. Đối với tôi, nó không thực sự hay cũng rất thanh lịch, vì vậy, tôi có xu hướng chỉ sử dụng nó để bẫy và báo cáo lỗi.
Tôi sẽ nắm bắt rằng PHP, perl, ruby, C và thậm chí cả cái vỏ kỳ dị có các chức năng đơn giản để kiểm tra một chuỗi cho số nguyên, nhưng do sự chuyên cần trong việc xác minh những giả định đó đã làm tôi vấp ngã! Rõ ràng sự thiếu này là một căn bệnh phổ biến.
Đây là bản chỉnh sửa nhanh và bẩn của bài đăng của Bruno:
import sys, time, re
g_intRegex = re.compile(r"^([+-]?[1-9]\d*|0)$")
testvals = [
# integers
0, 1, -1, 1.0, -1.0,
'0', '0.','0.0', '1', '-1', '+1', '1.0', '-1.0', '+1.0', '06',
# non-integers
'abc 123',
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'},
# with spaces
' 0 ', ' 0.', ' .0','.01 '
]
def isInt_try(v):
try: i = int(v)
except: return False
return True
def isInt_str(v):
v = str(v).strip()
return v=='0' or (v if v.find('..') > -1 else v.lstrip('-+').rstrip('0').rstrip('.')).isdigit()
def isInt_re(v):
import re
if not hasattr(isInt_re, 'intRegex'):
isInt_re.intRegex = re.compile(r"^([+-]?[1-9]\d*|0)$")
return isInt_re.intRegex.match(str(v).strip()) is not None
def isInt_re2(v):
return g_intRegex.match(str(v).strip()) is not None
def check_int(s):
s = str(s)
if s[0] in ('-', '+'):
return s[1:].isdigit()
return s.isdigit()
def timeFunc(func, times):
t1 = time.time()
for n in range(times):
for v in testvals:
r = func(v)
t2 = time.time()
return t2 - t1
def testFuncs(funcs):
for func in funcs:
sys.stdout.write( "\t%s\t|" % func.__name__)
print()
for v in testvals:
if type(v) == type(''):
sys.stdout.write("'%s'" % v)
else:
sys.stdout.write("%s" % str(v))
for func in funcs:
sys.stdout.write( "\t\t%s\t|" % func(v))
sys.stdout.write("\r\n")
if __name__ == '__main__':
print()
print("tests..")
testFuncs((isInt_try, isInt_str, isInt_re, isInt_re2, check_int))
print()
print("timings..")
print("isInt_try: %6.4f" % timeFunc(isInt_try, 10000))
print("isInt_str: %6.4f" % timeFunc(isInt_str, 10000))
print("isInt_re: %6.4f" % timeFunc(isInt_re, 10000))
print("isInt_re2: %6.4f" % timeFunc(isInt_re2, 10000))
print("check_int: %6.4f" % timeFunc(check_int, 10000))
Dưới đây là kết quả so sánh hiệu suất:
timings..
isInt_try: 0.6426
isInt_str: 0.7382
isInt_re: 1.1156
isInt_re2: 0.5344
check_int: 0.3452
Phương pháp AC có thể quét nó một lần thông qua và được thực hiện. Phương pháp AC quét chuỗi một lần thông qua sẽ là Điều phải làm, tôi nghĩ vậy.
BIÊN TẬP:
Tôi đã cập nhật mã ở trên để hoạt động trong Python 3.5 và bao gồm hàm check_int từ câu trả lời được bình chọn nhiều nhất hiện nay và để sử dụng regex phổ biến nhất hiện tại mà tôi có thể tìm thấy để kiểm tra trình duyệt số nguyên. Regex này từ chối các chuỗi như 'abc 123'. Tôi đã thêm 'abc 123' làm giá trị thử nghiệm.
Điều thú vị đối với tôi là tại thời điểm này, KHÔNG CÓ các hàm được kiểm tra, bao gồm phương thức thử, hàm check_int phổ biến và biểu thức chính thức phổ biến nhất để kiểm tra trình số nguyên, trả về các câu trả lời đúng cho tất cả các giá trị kiểm tra (tốt, tùy thuộc vào những gì bạn nghĩ câu trả lời đúng; xem kết quả kiểm tra bên dưới).
Hàm int () tích hợp trong âm thầm cắt phần phân số của số dấu phẩy động và trả về phần nguyên trước số thập phân, trừ khi số dấu phẩy động đầu tiên được chuyển đổi thành một chuỗi.
Hàm check_int () trả về false cho các giá trị như 0.0 và 1.0 (về mặt kỹ thuật là số nguyên) và trả về true cho các giá trị như '06'.
Dưới đây là kết quả kiểm tra (Python 3.5) hiện tại:
isInt_try | isInt_str | isInt_re | isInt_re2 | check_int |
0 True | True | True | True | True |
1 True | True | True | True | True |
-1 True | True | True | True | True |
1.0 True | True | False | False | False |
-1.0 True | True | False | False | False |
'0' True | True | True | True | True |
'0.' False | True | False | False | False |
'0.0' False | True | False | False | False |
'1' True | True | True | True | True |
'-1' True | True | True | True | True |
'+1' True | True | True | True | True |
'1.0' False | True | False | False | False |
'-1.0' False | True | False | False | False |
'+1.0' False | True | False | False | False |
'06' True | True | False | False | True |
'abc 123' False | False | False | False | False |
1.1 True | False | False | False | False |
-1.1 True | False | False | False | False |
'1.1' False | False | False | False | False |
'-1.1' False | False | False | False | False |
'+1.1' False | False | False | False | False |
'1.1.1' False | False | False | False | False |
'1.1.0' False | False | False | False | False |
'1.0.1' False | False | False | False | False |
'1.0.0' False | False | False | False | False |
'1.0.' False | False | False | False | False |
'1..0' False | False | False | False | False |
'1..' False | False | False | False | False |
'0.0.' False | False | False | False | False |
'0..0' False | False | False | False | False |
'0..' False | False | False | False | False |
'one' False | False | False | False | False |
<obj..> False | False | False | False | False |
(1, 2, 3) False | False | False | False | False |
[1, 2, 3] False | False | False | False | False |
{'one': 'two'} False | False | False | False | False |
' 0 ' True | True | True | True | False |
' 0.' False | True | False | False | False |
' .0' False | False | False | False | False |
'.01 ' False | False | False | False | False |
Bây giờ tôi đã thử thêm chức năng này:
def isInt_float(s):
try:
return float(str(s)).is_integer()
except:
return False
Nó thực hiện gần như cũng như check_int (0.3486) và nó trả về true cho các giá trị như 1.0 và 0.0 và +1.0 và 0. và .0, v.v. Nhưng nó cũng trả về đúng cho '06', vì vậy. Chọn chất độc của bạn, tôi đoán.
try
hiệu quả hơn: isInt_try: 0.6552 / isInt_str: 0.6394 / isInt num: 1.0296 / isInt num2: 0.5168.
str.isdigit()
nên làm thủ thuật.
Ví dụ:
str.isdigit("23") ## True
str.isdigit("abc") ## False
str.isdigit("23.4") ## False
EDIT : Như @BuzzMoschetti đã chỉ ra, cách này sẽ không thành công cho số trừ (ví dụ: "-23" ). Trong trường hợp input_num của bạn có thể nhỏ hơn 0, hãy sử dụng re.sub (regex_search, regex numplace, nội dung) trước khi áp dụng str.itorigit () . Ví dụ:
import re
input_num = "-23"
input_num = re.sub("^-", "", input_num) ## "^" indicates to remove the first "-" only
str.isdigit(input_num) ## True
Sử dụng một biểu thức thông thường:
import re
def RepresentsInt(s):
return re.match(r"[-+]?\d+$", s) is not None
Nếu bạn cũng phải chấp nhận phân số thập phân:
def RepresentsInt(s):
return re.match(r"[-+]?\d+(\.0*)?$", s) is not None
Để cải thiện hiệu suất nếu bạn đang làm điều này thường xuyên, hãy biên dịch biểu thức chính quy chỉ một lần bằng cách sử dụng re.compile()
.
Giải pháp RegEx phù hợp sẽ kết hợp các ý tưởng của Greg Hewgill và Nowell, nhưng không sử dụng biến toàn cục. Bạn có thể thực hiện điều này bằng cách đính kèm một thuộc tính cho phương thức. Ngoài ra, tôi biết rằng việc đưa nhập khẩu vào một phương thức là điều không nên, nhưng điều tôi sẽ làm là hiệu ứng "mô-đun lười biếng" như http://peak.telecommunity.com/DevCenter/Importing#lazy-imports
chỉnh sửa: Kỹ thuật yêu thích của tôi cho đến nay là sử dụng các phương thức độc quyền của đối tượng 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))
Và đối với các thành viên ít phiêu lưu trong lớp, đây là đầu ra:
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
>>> "+7".lstrip("-+").isdigit()
True
>>> "-7".lstrip("-+").isdigit()
True
>>> "7".lstrip("-+").isdigit()
True
>>> "13.4".lstrip("-+").isdigit()
False
Vì vậy, chức năng của bạn sẽ là:
def is_int(val):
return val[1].isdigit() and val.lstrip("-+").isdigit()
Cách tiếp cận của Greg Hewgill đã thiếu một vài thành phần: "^" hàng đầu để chỉ khớp với phần đầu của chuỗi và biên dịch lại từ trước. Nhưng phương pháp này sẽ cho phép bạn tránh thử: exept:
import re
INT_RE = re.compile(r"^[-]?\d+$")
def RepresentsInt(s):
return INT_RE.match(str(s)) is not None
Tôi sẽ quan tâm tại sao bạn đang cố gắng tránh thử: ngoại trừ?
Tôi phải làm điều này mọi lúc, và tôi có ác cảm nhẹ nhưng thừa nhận không hợp lý khi sử dụng mẫu thử / ngoại trừ. Tôi sử dụng cái này:
all([xi in '1234567890' for xi in x])
Nó không chứa các số âm, vì vậy bạn có thể loại bỏ một dấu trừ (nếu có), sau đó kiểm tra xem kết quả có bao gồm các chữ số từ 0-9 không:
all([xi in '1234567890' for xi in x.replace('-', '', 1)])
Bạn cũng có thể chuyển x sang str () nếu bạn không chắc chắn đầu vào là một chuỗi:
all([xi in '1234567890' for xi in str(x).replace('-', '', 1)])
Có ít nhất hai trường hợp (cạnh?) Khi điều này sụp đổ:
type(1E2)
đưa ra <class 'float'>
trong khi type(10^2)
đưa ra<class 'int'>
.Vì vậy, nó sẽ không hoạt động cho mọi đầu vào có thể, nhưng nếu bạn có thể loại trừ ký hiệu khoa học, ký hiệu số mũ và chuỗi rỗng, thì kiểm tra một dòng OK sẽ trả về False
nếu x không phải là số nguyên vàTrue
nếu x là số nguyên.
Tôi không biết nếu nó là pythonic, nhưng nó là một dòng, và nó tương đối rõ ràng những gì mã làm.
all(xi in '1234567890' for xi in x])
mô hình của tôi có vẻ giống như xin phép đi ngang qua bãi cỏ. Tôi không vui mừng khi trở thành người hỏi xin phép, nhưng chúng ta ở đây.
tôi nghĩ
s.startswith('-') and s[1:].isdigit()
sẽ tốt hơn để viết lại thành:
s.replace('-', '').isdigit()
bởi vì s [1:] cũng tạo ra một chuỗi mới
Nhưng giải pháp tốt hơn nhiều là
s.lstrip('+-').isdigit()
replace
? Ngoài ra, điều này sẽ chấp nhận không chính xác 5-2
, ví dụ.
s='-'
Tôi thực sự thích bài đăng của Shavais, nhưng tôi đã thêm một trường hợp thử nghiệm (& hàm isdigit () tích hợp):
def isInt_loop(v):
v = str(v).strip()
# swapping '0123456789' for '9876543210' makes nominal difference (might have because '1' is toward the beginning of the string)
numbers = '0123456789'
for i in v:
if i not in numbers:
return False
return True
def isInt_Digit(v):
v = str(v).strip()
return v.isdigit()
và nó liên tục đánh bại thời gian còn lại:
timings..
isInt_try: 0.4628
isInt_str: 0.3556
isInt_re: 0.4889
isInt_re2: 0.2726
isInt_loop: 0.1842
isInt_Digit: 0.1577
sử dụng trăn 2.7 bình thường:
$ python --version
Python 2.7.10
Cả hai trường hợp thử nghiệm mà tôi đã thêm (isInt_loop và isInt_digit) đều vượt qua các trường hợp thử nghiệm chính xác giống nhau (cả hai chỉ chấp nhận số nguyên không dấu), nhưng tôi nghĩ rằng mọi người có thể thông minh hơn với việc sửa đổi triển khai chuỗi (isInt_loop) trái ngược với mã số được xây dựng Hàm (), vì vậy tôi đã bao gồm nó, mặc dù có một chút khác biệt về thời gian thực hiện. (và cả hai phương pháp đánh bại mọi thứ khác rất nhiều, nhưng không xử lý các công cụ bổ sung: "./+/-")
Ngoài ra, tôi cũng thấy thú vị khi lưu ý rằng regex (phương thức isInt num2) đã đánh bại so sánh chuỗi trong cùng một bài kiểm tra đã được Shavais thực hiện vào năm 2012 (hiện tại 2018). Có lẽ các thư viện regex đã được cải thiện?
Đây có lẽ là cách đơn giản và pythonic nhất để tiếp cận nó theo ý kiến của tôi. Tôi đã không thấy giải pháp này và về cơ bản nó giống như regex, nhưng không có regex.
def is_int(test):
import string
return not (set(test) - set(string.digits))
set(input_string) == set(string.digits)
nếu chúng ta bỏ qua '-+ '
lúc bắt đầu và .0
, E-1
ở cuối.
Đây là một chức năng phân tích cú pháp mà không tăng lỗi. Nó xử lý các trường hợp rõ ràng trả None
về lỗi (xử lý các dấu hiệu 2000 '- / +' theo mặc định trên CPython!):
#!/usr/bin/env python
def get_int(number):
splits = number.split('.')
if len(splits) > 2:
# too many splits
return None
if len(splits) == 2 and splits[1]:
# handle decimal part recursively :-)
if get_int(splits[1]) != 0:
return None
int_part = splits[0].lstrip("+")
if int_part.startswith('-'):
# handle minus sign recursively :-)
return get_int(int_part[1:]) * -1
# successful 'and' returns last truth-y value (cast is always valid)
return int_part.isdigit() and int(int_part)
Một số xét nghiệm:
tests = ["0", "0.0", "0.1", "1", "1.1", "1.0", "-1", "-1.1", "-1.0", "-0", "--0", "---3", '.3', '--3.', "+13", "+-1.00", "--+123", "-0.000"]
for t in tests:
print "get_int(%s) = %s" % (t, get_int(str(t)))
Các kết quả:
get_int(0) = 0
get_int(0.0) = 0
get_int(0.1) = None
get_int(1) = 1
get_int(1.1) = None
get_int(1.0) = 1
get_int(-1) = -1
get_int(-1.1) = None
get_int(-1.0) = -1
get_int(-0) = 0
get_int(--0) = 0
get_int(---3) = -3
get_int(.3) = None
get_int(--3.) = 3
get_int(+13) = 13
get_int(+-1.00) = -1
get_int(--+123) = 123
get_int(-0.000) = 0
Đối với nhu cầu của bạn, bạn có thể sử dụng:
def int_predicate(number):
return get_int(number) is not None
Tôi đề nghị như sau:
import ast
def is_int(s):
return isinstance(ast.literal_eval(s), int)
Từ các tài liệu :
Đánh giá một cách an toàn một nút biểu thức hoặc một chuỗi chứa màn hình hiển thị bằng chữ hoặc container của Python. Chuỗi hoặc nút được cung cấp chỉ có thể bao gồm các cấu trúc chữ Python sau: chuỗi, byte, số, bộ dữ liệu, danh sách, ký tự, bộ, booleans và Không có.
Tôi nên lưu ý rằng điều này sẽ đưa ra một ValueError
ngoại lệ khi được gọi chống lại bất cứ thứ gì không cấu thành một chữ Python. Vì câu hỏi yêu cầu giải pháp mà không cần thử / ngoại trừ, tôi có giải pháp loại Kobayashi-Maru cho điều đó:
from ast import literal_eval
from contextlib import suppress
def is_int(s):
with suppress(ValueError):
return isinstance(literal_eval(s), int)
return False
¯ \ _ (ツ) _ /
Tôi đoán câu hỏi có liên quan đến tốc độ vì thử / ngoại trừ có hình phạt thời gian:
Đầu tiên, tôi tạo một danh sách gồm 200 chuỗi, 100 chuỗi không thành công và 100 chuỗi số.
from random import shuffle
numbers = [u'+1'] * 100
nonumbers = [u'1abc'] * 100
testlist = numbers + nonumbers
shuffle(testlist)
testlist = np.array(testlist)
np.core.defchararray.isnumeric cũng có thể hoạt động với các chuỗi unicode np.core.defchararray.isnumeric(u'+12')
nhưng nó trả về và mảng. Vì vậy, đó là một giải pháp tốt nếu bạn phải thực hiện hàng ngàn chuyển đổi và thiếu dữ liệu hoặc dữ liệu không phải là số.
import numpy as np
%timeit np.core.defchararray.isnumeric(testlist)
10000 loops, best of 3: 27.9 µs per loop # 200 numbers per loop
def check_num(s):
try:
int(s)
return True
except:
return False
def check_list(l):
return [check_num(e) for e in l]
%timeit check_list(testlist)
1000 loops, best of 3: 217 µs per loop # 200 numbers per loop
Có vẻ như giải pháp numpy là nhanh hơn nhiều.
Nếu bạn chỉ muốn chấp nhận các chữ số ascii thấp hơn, đây là các thử nghiệm để làm như vậy:
Python 3.7+: (u.isdecimal() and u.isascii())
Con trăn <= 3.6: (u.isdecimal() and u == str(int(u)))
Các câu trả lời khác đề nghị sử dụng .isdigit()
hoặc .isdecimal()
nhưng cả hai đều bao gồm một số ký tự unicode trên, chẳng hạn như '٢'
( u'\u0662'
):
u = u'\u0662' # '٢'
u.isdigit() # True
u.isdecimal() # True
u.isascii() # False (Python 3.7+ only)
u == str(int(u)) # False
int()
.
Uh .. Hãy thử điều này:
def int_check(a):
if int(a) == a:
return True
else:
return False
Điều này hoạt động nếu bạn không đặt một chuỗi không phải là một số.
Và cũng (tôi quên đặt phần kiểm tra số.), Có một chức năng kiểm tra xem chuỗi có phải là số hay không. Đó là str.itorigit (). Đây là một ví dụ:
a = 2
a.isdigit()
Nếu bạn gọi a.itorigit (), nó sẽ trả về True.
2
được gán a
.