Czy jest jakiś sposób na stwierdzenie, czy łańcuch reprezentuje liczbę całkowitą (np. '3'
, '-17'
Ale nie ) '3.14'
lub 'asfasfas'
bez użycia mechanizmu try / wyj?
is_int('3.14') = False
is_int('-7') = True
Czy jest jakiś sposób na stwierdzenie, czy łańcuch reprezentuje liczbę całkowitą (np. '3'
, '-17'
Ale nie ) '3.14'
lub 'asfasfas'
bez użycia mechanizmu try / wyj?
is_int('3.14') = False
is_int('-7') = True
Odpowiedzi:
Jeśli naprawdę denerwujesz się używaniem try/except
s wszędzie, napisz po prostu funkcję pomocnika:
def RepresentsInt(s):
try:
int(s)
return True
except ValueError:
return False
>>> print RepresentsInt("+123")
True
>>> print RepresentsInt("10.0")
False
Będzie znacznie więcej kodu, aby dokładnie obejmował wszystkie ciągi, które Python uważa za liczby całkowite. Mówię, po prostu bądź w tym pythonie.
>>> print RepresentsInt(10.0)
True
>>> print RepresentsInt(10.06)
True
z dodatnimi liczbami całkowitymi, których możesz użyć .isdigit
:
>>> '16'.isdigit()
True
nie działa jednak z ujemnymi liczbami całkowitymi. załóżmy, że możesz spróbować:
>>> s = '-17'
>>> s.startswith('-') and s[1:].isdigit()
True
nie będzie działać z '16.0'
formatem, który jest podobny doint
w tym sensie rzutowanie.
edycja :
def check_int(s):
if s[0] in ('-', '+'):
return s[1:].isdigit()
return s.isdigit()
u'²'.isdigit()
jest prawdziwe, ale int(u'²')
podnosi wartość błędu. Użyj u.isdecimal()
zamiast tego. str.isdigit()
zależy od ustawień regionalnych w języku Python 2.
check_int('')
podniesie wyjątek zamiast powrotuFalse
Wiesz, znalazłem (i testowałem to w kółko), że próba / wyjątek nie działa tak dobrze, z jakiegokolwiek powodu. Często próbuję różnych sposobów robienia rzeczy i nie sądzę, że kiedykolwiek znalazłem metodę, która używa try / try do wykonywania najlepszych z testowanych, w rzeczywistości wydaje mi się, że metody te zwykle są zbliżone do najgorsze, jeśli nie najgorsze. Nie w każdym przypadku, ale w wielu przypadkach. Wiem, że wiele osób twierdzi, że jest to „Pythoński” sposób, ale w tym obszarze się z nimi rozstaję. Dla mnie nie jest ani bardzo wydajny, ani bardzo elegancki, więc zwykle używam go tylko do wychwytywania błędów i raportowania.
Chciałem się przekonać, że PHP, perl, ruby, C, a nawet dziwaczna powłoka mają proste funkcje do testowania łańcucha dla liczb całkowitych, ale zachowałem należytą staranność przy weryfikacji tych założeń! Najwyraźniej ten brak jest powszechną chorobą.
Oto szybka i brudna edycja postu 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))
Oto wyniki porównania wydajności:
timings..
isInt_try: 0.6426
isInt_str: 0.7382
isInt_re: 1.1156
isInt_re2: 0.5344
check_int: 0.3452
Metoda AC może zeskanować to raz i gotowe. Myślę, że metoda AC, która raz skanuje łańcuch, byłaby właściwa.
EDYTOWAĆ:
Zaktualizowałem powyższy kod, aby działał w Pythonie 3.5, i zawierał funkcję check_int z obecnie najczęściej głosowanej odpowiedzi, a także do użycia najpopularniejszego wyrażenia regularnego, jakie mogę znaleźć do testowania pod kątem liczby całkowitej. Wyrażenie regularne odrzuca ciągi takie jak „abc 123”. Dodałem „abc 123” jako wartość testową.
Bardzo interesujące jest dla mnie odnotowanie w tym momencie, ŻADNA z testowanych funkcji, w tym metody try, popularnej funkcji check_int i najpopularniejszego wyrażenia regularnego do testowania liczby całkowitej, zwraca poprawne odpowiedzi dla wszystkich wartości testowe (cóż, w zależności od tego, co uważasz za poprawne odpowiedzi; patrz wyniki testu poniżej).
Wbudowana funkcja int () po cichu obcina ułamkową część liczby zmiennoprzecinkowej i zwraca część całkowitą przed przecinkiem, chyba że liczba zmiennoprzecinkowa jest najpierw konwertowana na ciąg.
Funkcja check_int () zwraca false dla wartości takich jak 0.0 i 1.0 (które technicznie są liczbami całkowitymi) i zwraca true dla wartości takich jak „06”.
Oto aktualne wyniki testu (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 |
Właśnie próbowałem dodać tę funkcję:
def isInt_float(s):
try:
return float(str(s)).is_integer()
except:
return False
Działa prawie tak samo dobrze jak check_int (0.3486) i zwraca true dla wartości takich jak 1.0 i 0.0 oraz +1.0 i 0. i .0 i tak dalej. Ale zwraca również prawdę dla „06”, więc. Chyba wybierz swoją truciznę.
try
jest bardziej wydajny: isInt_try: 0.6552 / isInt_str: 0.6396 / isInt_re: 1.0296 / isInt_re2: 0.5168.
str.isdigit()
powinien załatwić sprawę.
Przykłady:
str.isdigit("23") ## True
str.isdigit("abc") ## False
str.isdigit("23.4") ## False
EDYCJA : Jak wskazał @BuzzMoschetti, ten sposób zakończy się niepowodzeniem dla liczby ujemnej (np. „-23” ). Jeśli twoja liczba_ wejściowa może być mniejsza niż 0, użyj re.sub (regex_search, regex_replace, content) przed zastosowaniem str.isdigit () . Na przykład:
import re
input_num = "-23"
input_num = re.sub("^-", "", input_num) ## "^" indicates to remove the first "-" only
str.isdigit(input_num) ## True
Użyj wyrażenia regularnego:
import re
def RepresentsInt(s):
return re.match(r"[-+]?\d+$", s) is not None
Jeśli musisz zaakceptować ułamki dziesiętne:
def RepresentsInt(s):
return re.match(r"[-+]?\d+(\.0*)?$", s) is not None
Aby zwiększyć wydajność, jeśli robisz to często, skompiluj wyrażenie regularne tylko raz re.compile()
.
Właściwe rozwiązanie RegEx połączyłoby pomysły Grega Hewgilla i Nowella, ale nie używałoby zmiennej globalnej. Możesz to zrobić, dołączając atrybut do metody. Wiem też, że marnowanie wartości w metodzie jest niezadowolone, ale zamierzam uzyskać efekt „leniwego modułu”, takiego jak http://peak.telecommunity.com/DevCenter/Importing#lazy-imports
edycja: Jak dotąd moją ulubioną techniką jest stosowanie wyłącznie metod obiektu 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))
A dla mniej ryzykownych członków klasy oto wynik:
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
Więc twoją funkcją byłoby:
def is_int(val):
return val[1].isdigit() and val.lstrip("-+").isdigit()
W podejściu Grega Hewgilla brakowało kilku elementów: wiodącego „^”, który pasuje tylko do początku łańcucha, i wcześniej kompiluje się ponownie. Ale to podejście pozwoli ci uniknąć próby: z wyjątkiem:
import re
INT_RE = re.compile(r"^[-]?\d+$")
def RepresentsInt(s):
return INT_RE.match(str(s)) is not None
Byłbym zainteresowany, dlaczego próbujesz uniknąć próby: z wyjątkiem?
Muszę to robić cały czas i mam łagodną, ale niewątpliwie irracjonalną niechęć do używania schematu try / wyjątkiem. Używam tego:
all([xi in '1234567890' for xi in x])
Nie obsługuje liczb ujemnych, więc możesz usunąć jeden znak minus (jeśli istnieje), a następnie sprawdzić, czy wynik zawiera cyfry od 0 do 9:
all([xi in '1234567890' for xi in x.replace('-', '', 1)])
Możesz także przekazać x do str (), jeśli nie jesteś pewien, czy dane wejściowe są ciągiem znaków:
all([xi in '1234567890' for xi in str(x).replace('-', '', 1)])
Istnieją co najmniej dwa przypadki (krawędzi?), W których się to rozpada:
type(1E2)
daje, <class 'float'>
a type(10^2)
daje<class 'int'>
.Nie będzie więc działać przy każdym możliwym wprowadzeniu, ale jeśli można wykluczyć notację naukową, wykładniczą i puste ciągi znaków, jest to sprawdzanie OK w jednym wierszu, które zwraca, False
jeśli x nie jest liczbą całkowitą iTrue
jeśli x jest liczbą całkowitą.
Nie wiem, czy jest to python, ale jest to jedna linia i relatywnie jasne jest, co robi kod.
all(xi in '1234567890' for xi in x])
wzór bardziej przypomina prośbę o pozwolenie na spacer po trawniku. Nie jestem podekscytowany tym, że pytam o pozwolenie, ale oto jesteśmy.
Myślę
s.startswith('-') and s[1:].isdigit()
lepiej byłoby przepisać na:
s.replace('-', '').isdigit()
ponieważ s [1:] tworzy również nowy ciąg
Ale jest o wiele lepsze rozwiązanie
s.lstrip('+-').isdigit()
replace
robi? Ponadto, 5-2
na przykład niepoprawnie to zaakceptuje .
s='-'
Naprawdę podobał mi się post Shavaisa, ale dodałem jeszcze jedną walizkę testową (i wbudowaną funkcję 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()
i znacząco konsekwentnie bije czasy reszty:
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
używając normalnego pytona 2.7:
$ python --version
Python 2.7.10
Oba dodane przeze mnie przypadki testowe (isInt_loop i isInt_digit) przekazują dokładnie te same przypadki testowe (oba akceptują tylko liczby całkowite bez znaku), ale pomyślałem, że ludzie mogą być bardziej sprytni, modyfikując implementację łańcucha (isInt_loop) w przeciwieństwie do wbudowanego isdigit (), więc zawarłem ją, mimo że istnieje niewielka różnica w czasie wykonywania. (i obie metody znacznie pobiły wszystko inne, ale nie radzą sobie z dodatkowymi rzeczami: „./+/-”)
Ciekawe było też to, że regex (metoda isInt_re2) pokonał porównanie ciągów w tym samym teście, który przeprowadził Shavais w 2012 roku (obecnie 2018). Może biblioteki regex zostały ulepszone?
Moim zdaniem jest to prawdopodobnie najprostszy i pytonowy sposób podejścia do tego problemu. Nie widziałem tego rozwiązania i jest zasadniczo takie samo jak regex, ale bez regex.
def is_int(test):
import string
return not (set(test) - set(string.digits))
set(input_string) == set(string.digits)
jeśli pominąć '-+ '
na początku i .0
, E-1
na końcu.
Oto funkcja, która analizuje bez zgłaszania błędów. Obsługuje oczywiste przypadki powrotu None
po awarii (domyślnie obsługuje znaki do 2000 '- / +' w 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)
Niektóre testy:
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)))
Wyniki:
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
Do swoich potrzeb możesz użyć:
def int_predicate(number):
return get_int(number) is not None
Proponuję następujące:
import ast
def is_int(s):
return isinstance(ast.literal_eval(s), int)
Z dokumentów :
Bezpiecznie oceń węzeł wyrażenia lub ciąg znaków zawierający literał Pythona lub wyświetlacz kontenera. Podany ciąg znaków lub węzeł może składać się tylko z następujących struktur literału Pythona: ciągów, bajtów, liczb, krotek, list, dykt, zestawów, logicznych i Brak.
Powinienem zauważyć, że spowoduje to ValueError
wyjątek, gdy zostanie wywołany przeciwko cokolwiek, co nie stanowi literału Pythona. Ponieważ pytanie zadawało rozwiązanie bez wypróbowania / z wyjątkiem, mam do tego rozwiązanie typu 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
¯ \ _ (ツ) _ / ¯
Myślę, że pytanie dotyczy prędkości, ponieważ próba / wyjątek ma karę czasową:
Najpierw utworzyłem listę 200 ciągów, 100 niedziałających i 100 ciągów numerycznych.
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 może również działać z ciągami znaków Unicode, np.core.defchararray.isnumeric(u'+12')
ale zwraca tablicę. Jest to dobre rozwiązanie, jeśli musisz wykonać tysiące konwersji i brakuje danych lub danych nienumerycznych.
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
Wydaje się, że numpy rozwiązanie jest znacznie szybsze.
Jeśli chcesz akceptować tylko cyfry o niższej ascii, oto testy, aby to zrobić:
Python 3.7+: (u.isdecimal() and u.isascii())
Python <= 3.6: (u.isdecimal() and u == str(int(u)))
Inne odpowiedzi sugerujemy użycie .isdigit()
lub .isdecimal()
ale są zarówno zawierać górny-unicode znaki takie jak '٢'
( 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 .. Spróbuj tego:
def int_check(a):
if int(a) == a:
return True
else:
return False
Działa to, jeśli nie wstawisz łańcucha, który nie jest liczbą.
A także (zapomniałem umieścić część sprawdzania liczby). Istnieje funkcja sprawdzająca, czy ciąg jest liczbą, czy nie. Jest to str.isdigit (). Oto przykład:
a = 2
a.isdigit()
Wywołanie a.isdigit () zwróci True.
2
przypisanej wartości a
.