Можно ли указать, является ли строка целым числом (например, '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
Если вы действительно раздражены при использовании try/except
повсюду, просто напишите вспомогательную функцию:
def RepresentsInt(s):
try:
int(s)
return True
except ValueError:
return False
>>> print RepresentsInt("+123")
True
>>> print RepresentsInt("10.0")
False
Это будет ПУТЬ больше кода, чтобы точно покрыть все строки, которые Python считает целыми числами. Я говорю только о том, чтобы быть питоническим на этом.
с положительными целыми числами вы можете использовать .isdigit
:
>>> '16'.isdigit()
True
он не работает с отрицательными целыми числами. предположим, что вы можете попробовать следующее:
>>> s = '-17'
>>> s.startswith('-') and s[1:].isdigit()
True
он не будет работать с форматом '16.0'
, который похож на кастинг int
в этом смысле.
изменить
def check_int(s):
if s[0] in ('-', '+'):
return s[1:].isdigit()
return s.isdigit()
Вы знаете, я нашел (и я тестировал это снова и снова), которые пытаются/за исключением того, что не выполняют все это по какой-либо причине. Я часто пытаюсь несколько способов сделать что-то, и я не думаю, что когда-либо нашел метод, который использует try/except для выполнения лучших из тестируемых, на самом деле мне кажется, что эти методы обычно выходят близко к худшее, если не самое худшее. Не во всех случаях, а во многих случаях. Я знаю, что многие люди говорят, что это "питонический" способ, но это одна область, где я расстаюсь с ними. Для меня это не очень показательно и не очень элегантно, поэтому я стараюсь использовать его только для захвата ошибок и отчетности.
Я собирался понять, что PHP, perl, ruby, C и даже freaking shell имеют простые функции для тестирования строки для целочисленного капота, но должная осмотрительность в проверке того, что эти предположения подстегнула меня! Видимо, это недостаток - обычная болезнь.
Здесь быстрое и грязное редактирование сообщения Бруно:
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))
Вот результаты сравнения производительности:
timings..
isInt_try: 0.6426
isInt_str: 0.7382
isInt_re: 1.1156
isInt_re2: 0.5344
check_int: 0.3452
Метод AC может сканировать его один раз через, и будет сделано. По-моему, метод AC, который сканирует строку один раз, будет правильным делом.
РЕДАКТИРОВАТЬ:
Я обновил приведенный выше код для работы в Python 3.5 и включил функцию check_int из текущего наиболее голосового ответа и использовал текущее самое популярное регулярное выражение, которое можно найти для тестирования для целочисленного капота. Это регулярное выражение отклоняет строки типа 'abc 123'. Я добавил "abc 123" в качестве тестового значения.
Мне очень интересно отметить, что на данный момент NONE проверенных функций, включая метод try, популярную функцию check_int и самое популярное регулярное выражение для тестирования для целочисленного капота, вернет правильные ответы для всех тестовые значения (ну, в зависимости от того, что вы считаете правильными ответами, см. результаты теста ниже).
Встроенная функция int() молча усекает дробную часть числа с плавающей запятой и возвращает целочисленную часть перед десятичной точкой, если только число с плавающей запятой сначала не преобразуется в строку.
Функция check_int() возвращает false для значений, таких как 0.0 и 1.0 (которые технически являются целыми числами) и возвращает true для значений типа "06".
Вот текущие результаты теста (Python 3.5):
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 |
Только сейчас я попытался добавить эту функцию:
def isInt_float(s):
try:
return float(str(s)).is_integer()
except:
return False
Он выполняет почти так же, как check_int (0.3486), и возвращает true для значений типа 1.0 и 0.0 и +1.0 и 0. и.0 и так далее. Но он также возвращает true для '06', так что. Выбери свой яд, я думаю.
Используйте регулярное выражение:
import re
def RepresentsInt(s):
return re.match(r"[-+]?\d+$", s) is not None
Если вы также должны принимать десятичные дроби:
def RepresentsInt(s):
return re.match(r"[-+]?\d+(\.0*)?$", s) is not None
Для повышения производительности, если вы часто это делаете, скомпилируйте регулярное выражение только один раз, используя re.compile()
.
str.isdigit()
должен сделать str.isdigit()
дело.
Примеры:
str.isdigit("23") ## True str.isdigit("abc") ## False str.isdigit("23.4") ## False
РЕДАКТИРОВАТЬ: Как @BuzzMoschetti указал, этот путь не будет работать для минус число (например, "-23"). Если ваш input_num может быть меньше 0, используйте re.sub(regex_search, regex_replace, contents) перед применением str.isdigit(). Например:
import re input_num="-23" input_num = re.sub("^-", "", input_num) # "^" indicates to remove the first "-" only. str.isdigit(input_num) ## True
Правильное решение RegEx объединит идеи Грега Хьюджилла и Новелла, но не использует глобальную переменную. Вы можете выполнить это, присоединив атрибут к методу. Кроме того, я знаю, что он не согласен вносить импорт в метод, но то, что я собираюсь, это эффект "ленивого модуля", например 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
В подходе Грега Хьюджилла отсутствовали несколько компонентов: ведущий "^", чтобы соответствовать началу строки, и компилировать повтор заранее. Но этот подход позволит вам избежать попытки: кроме:
import re
INT_RE = re.compile(r"^[-]?\d+$")
def RepresentsInt(s):
return INT_RE.match(str(s)) is not None
Мне было бы интересно, почему вы пытаетесь избежать try: except?
>>> "+7".lstrip("-+").isdigit()
True
>>> "-7".lstrip("-+").isdigit()
True
>>> "7".lstrip("-+").isdigit()
True
>>> "13.4".lstrip("-+").isdigit()
False
Таким образом, ваша функция будет:
def is_int(val):
return val[1].isdigit() and val.lstrip("-+").isdigit()
Я думаю,
s.startswith('-') and s[1:].isdigit()
лучше переписать на:
s.replace('-', '').isdigit()
потому что s [1:] также создает новую строку
Но гораздо лучшее решение -
s.lstrip('+-').isdigit()
Мне очень понравилось сообщение Shavais, но я добавил еще один тестовый пример (и встроенная функция isdigit()):
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()
и он значительно последовательно превосходит время остальных:
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
используя обычный 2,7-питон:
$ python --version
Python 2.7.10
Оба двух тестовых примера, которые я добавил (isInt_loop и isInt_digit), передают одни и те же тестовые примеры (оба они принимают только целые числа без знака), но я думал, что люди могут быть более умными с модификацией реализации строки (isInt_loop), противоположной встроенной isdigit(), поэтому я включил его, несмотря на небольшую разницу во времени выполнения. (и оба метода сильно избили все остальное, но не обрабатывают дополнительный материал: "./+ / -")
Кроме того, мне было интересно заметить, что метод regex (isInt_re2) превзошел сравнение строк в том же тесте, который был выполнен Shavais в 2012 году (в настоящее время 2018). Возможно, библиотеки регулярных выражений были улучшены?
Это, пожалуй, самый простой и питонический подход к моему мнению. Я не видел этого решения, и это в основном то же самое, что и регулярное выражение, но без регулярного выражения.
def is_int(test):
import string
return not (set(test) - set(string.digits))
Вот функция, которая анализирует без ошибок. Он обрабатывает очевидные случаи, возвращает None
при сбое (по умолчанию на CPython обрабатывает знаки до 2000 "-/+" ):
#!/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)
Некоторые тесты:
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)))
Результаты:
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
Для ваших нужд вы можете использовать:
def int_predicate(number):
return get_int(number) is not None
У меня есть одна возможность, которая не использует int вообще, и не должна поднимать исключение, если строка не представляет число
float(number)==float(number)//1
Он должен работать для любой строки, которая принимает float, положительную, отрицательную, техническую нотацию...
Я предполагаю, что вопрос связан со скоростью, так как попытка/исключение имеет штраф за время:
Сначала я создал список из 200 строк, 100 строк с ошибками и 100 строк с цифрами.
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 также может работать со строками Unicode np.core.defchararray.isnumeric(u'+12')
но возвращает и массив. Таким образом, это хорошее решение, если вам нужно выполнить тысячи преобразований и у вас отсутствуют данные или не числовые данные.
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
Кажется, что решение NumPy намного быстрее.
Если вы хотите принимать только младшие цифры, вот тесты для этого:
Python 3. 7+: (u.isdecimal() and u.isascii())
Python & lt; = 3.6: (u.isdecimal() and u == str(int(u)))
В других ответах предлагается использовать .isdigit()
или .isdecimal()
, но оба содержат символы верхнего юникода, например '٢'
(u'\u0662'
):
u = u'\u0662' # '٢'
u.isdigit() # True
u.isdecimal() # True
u.isascii() # False (Python 3.7+ only)
u == str(int(u)) # False
Ух.. Попробуйте следующее:
def int_check(a):
if int(a) == a:
return True
else:
return False
Это работает, если вы не поместите строку, а не число.
А также (я забыл поставить часть проверки числа.), есть функция, проверяющая, является ли строка числом или нет. Это str.isdigit(). Вот пример:
a = 2
a.isdigit()
Если вы вызываете a.isdigit(), он вернет True.