Чи є спосіб визначити, чи являє собою рядок ціле число (наприклад '3'
, '-17'
але немає '3.14'
чи 'asfasfas'
), не використовуючи механізм спробу / виключення?
is_int('3.14') = False
is_int('-7') = True
Чи є спосіб визначити, чи являє собою рядок ціле число (наприклад '3'
, '-17'
але немає '3.14'
чи 'asfasfas'
), не використовуючи механізм спробу / виключення?
is_int('3.14') = False
is_int('-7') = True
Відповіді:
Якщо вас справді просто дратує використання try/except
s у всьому місці, будь ласка, просто напишіть функцію помічника:
def RepresentsInt(s):
try:
int(s)
return True
except ValueError:
return False
>>> print RepresentsInt("+123")
True
>>> print RepresentsInt("10.0")
False
Це буде НАЙКРАЩИЙ код, щоб точно покрити всі рядки, які Python вважає цілими числами. Я кажу, просто будьте пітонічним на цьому.
>>> print RepresentsInt(10.0)
True
>>> print RepresentsInt(10.06)
True
з натуральними числами, які ви можете використовувати .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()
u'²'.isdigit()
вірно, але int(u'²')
підвищує ValueError. Використовуйте u.isdecimal()
замість цього. str.isdigit()
є
check_int('')
підніме виняток замість поверненняFalse
Ви знаєте, я виявив (і перевіряв це знову і знову), що спробувати / за винятком причин не працює все так добре. Я часто пробую кілька способів робити речі, і я не думаю, що я ніколи не знаходив метод, який використовує спробу / крім того, щоб виконати найкращі з перевірених, насправді мені здається, що ці методи зазвичай виходять близькими до найгірше, якщо не найгірше. Не в кожному випадку, але у багатьох випадках. Я знаю, що багато людей кажуть, що це "піфонічний" шлях, але це одна сфера, де я розлучаюся з ними. Для мене це не дуже ефектно і не дуже елегантно, тому я, як правило, використовую його лише для лову помилок та звітування.
Я збирався зрозуміти, що PHP, perl, ruby, C і навіть кричуща оболонка мають прості функції для тестування рядка для цілочистої кришки, але належна ретельність щодо перевірки цих припущень викликала мене! Мабуть, ця відсутність є звичайною хворобою.
Ось швидка та брудна редакція публікації Бруно:
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
Метод змінного струму міг би просканувати його один раз і зробити це. Метод змінного струму, який сканує рядок один раз, був би правильним питанням, я думаю.
Редагувати:
Я оновив вищевказаний код для роботи в Python 3.5 і включав функцію check_int із найбільш відповідної на даний момент відповіді, а також використовувати поточний найпопулярніший регулярний вираз, який я можу знайти для тестування на цілі численні кришки. Цей регулярний вираз відхиляє рядки типу "abc 123". Я додав 'abc 123' як тестове значення.
Мені дуже цікаво зазначити, що на даний момент НІКОЛЬ з перевірених функцій, включаючи метод спробу, популярну функцію 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), і повертає істинні значення, такі як 1,0 і 0,0, +1,0 і 0, .0 і так далі. Але це також повертає істину для "06", так Збирай свою отруту, гадаю.
try
ефективніше: isInt_try: 0.6552 / isInt_str: 0.6396 / isInt_re: 1.0296 / isInt_re2: 0.5168.
str.isdigit()
повинен зробити трюк.
Приклади:
str.isdigit("23") ## True
str.isdigit("abc") ## False
str.isdigit("23.4") ## False
EDIT : Як зазначав @BuzzMoschetti, цей спосіб не матиме мінусового числа (наприклад, "-23" ). Якщо ваш input_num може бути меншим за 0, використовуйте re.sub (regex_search, regex_replace, content) перед застосуванням str.isdigit () . Наприклад:
import re
input_num = "-23"
input_num = re.sub("^-", "", input_num) ## "^" indicates to remove the first "-" only
str.isdigit(input_num) ## True
Використовуйте регулярний вираз:
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()
.
Правильне рішення RegEx поєднало б ідеї Грега Хьюгілла та Ноуелла, але не використовувало глобальну змінну. Ви можете досягти цього, приєднавши атрибут до методу. Крім того, я знаю, що нахмуритися вводити імпорт методом, але те, що я збираюся, - це ефект "ледачого модуля", як http://peak.telecommunity.com/DevCenter/Importing#lazy-imports
редагувати: Моя улюблена методика поки що - використовувати виключно методи об’єкта 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
>>> "+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()
У підході Грега Хьюгілла бракувало декількох компонентів: провідний "^", щоб відповідати лише початку рядка, і попередньо складати повторно. Але такий підхід дозволить вам уникнути спроби: exept:
import re
INT_RE = re.compile(r"^[-]?\d+$")
def RepresentsInt(s):
return INT_RE.match(str(s)) is not None
Мені було б цікаво, чому ви намагаєтеся уникати спроб: хіба що?
Я повинен робити це постійно, і у мене є легка, але, очевидно, ірраціональна відраза до використання спроби / за винятком шаблону. Я використовую це:
all([xi in '1234567890' for xi in x])
Він не містить від'ємних чисел, тому ви можете викреслити один знак мінус (якщо такий є), а потім перевірити, чи містить результат цифри від 0 до 9:
all([xi in '1234567890' for xi in x.replace('-', '', 1)])
Ви також можете передати x до str (), якщо не впевнені, що введення є рядком:
all([xi in '1234567890' for xi in str(x).replace('-', '', 1)])
Є щонайменше два (крайні?) Випадки, коли це розпадається:
type(1E2)
дає, <class 'float'>
хоча type(10^2)
дає<class 'int'>
.Таким чином, він не працюватиме для кожного можливого введення, але якщо ви можете виключити наукові позначення, експоненціальні позначення та порожні рядки, це однорядкова перевірка ОК, яка повертається, False
якщо x не є цілим числом іTrue
якщо x - цілим числом.
Я не знаю, чи це пітонічно, але це один рядок, і відносно зрозуміло, що робить код.
all(xi in '1234567890' for xi in x])
зразок здається більше схожим на прохання дозволу ходити по газоні. Я не в захваті від запитувача дозволу, але ось ми.
я думаю
s.startswith('-') and s[1:].isdigit()
було б краще переписати на:
s.replace('-', '').isdigit()
тому що s [1:] також створює новий рядок
Але набагато краще рішення
s.lstrip('+-').isdigit()
replace
робить? Також це неправильно прийме 5-2
, наприклад.
s='-'
Мені дуже сподобався пост Шаве, але я додав ще один тестовий випадок (і вбудовану функцію 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 () функцію, тому я включив її, хоча незначна різниця у часі виконання. (і обидва способи багато чого перемогли, але не обробляйте зайві речі: "./+/-")
Крім того, мені було цікаво відзначити, що регекс (метод isInt_re2) переміг порівняння рядків у тому ж тесті, який виконував Шавай у 2012 році (наразі 2018). Може бути покращено бібліотеки регулярних виразів?
На мою думку, це, мабуть, найпростіший і пітонічний спосіб наблизитись до цього. Я не бачив цього рішення, і це в основному те саме, що і регулярний вираз, але без регулярного вираження.
def is_int(test):
import string
return not (set(test) - set(string.digits))
set(input_string) == set(string.digits)
якщо ми пропустимо '-+ '
на початку і.0
, E-1
врешті-решт.
Ось функція, яка аналізує, не викликаючи помилок. Він обробляє очевидні випадки повернення None
при відмові (обробляє до 2000 знаків '- / +' за замовчуванням на 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)
Деякі тести:
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
Я пропоную наступне:
import ast
def is_int(s):
return isinstance(ast.literal_eval(s), int)
З документів :
Безпечно оцініть вузол виразів або рядок, що містить літерал Python або відображення контейнера. Наданий рядок або вузол може складатися лише з таких буквальних структур Python: рядки, байти, числа, кортежі, списки, дикти, набори, булеві і None.
Слід зазначити, що це призведе до ValueError
винятку, коли закликається проти чогось, що не є літералом Python. Оскільки в запитанні було запропоновано рішення без спроб / крім, у мене є рішення типу Kobayashi-Maru для цього:
from ast import literal_eval
from contextlib import suppress
def is_int(s):
with suppress(ValueError):
return isinstance(literal_eval(s), int)
return False
¯ \ _ (ツ) _ / ¯
Я думаю, що питання пов'язане зі швидкістю, оскільки спроба / за винятком часового покарання:
По-перше, я створив список з 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
Здається, що нудотне рішення набагато швидше.
Якщо ви бажаєте приймати лише цифри з нижчим рівнем асції, ось такі тести:
Python 3.7+: (u.isdecimal() and u.isascii())
Python <= 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
int()
.
Спробуйте.
def int_check(a):
if int(a) == a:
return True
else:
return False
Це працює, якщо ви не ставите рядок, який не є числом.
А також (я забув покласти частину перевірки числа.), Є перевірка функції, чи рядок є числом чи ні. Це str.isdigit (). Ось приклад:
a = 2
a.isdigit()
Якщо ви зателефонуєте на a.isdigit (), він поверне значення True.
2
призначеного a
.