Jak mogę sprawdzić, czy ciąg reprezentuje liczbę całkowitą, bez użycia try / wyjątkiem?
466
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?
Dlaczego oboje próbują to zrobić „na twardo”? Co jest nie tak z próbą / wyjątkiem?
S.Lott,
5
Tak, co jest nie tak z próbą / wyjątkiem? Lepiej prosić o wybaczenie niż o pozwolenie.
mk12
53
Zapytałbym, dlaczego ta prosta rzecz powinna wymagać wypróbowania / wyjątku? System wyjątków to złożona bestia, ale jest to prosty problem.
Aivar,
13
@Aivar przestań rozpowszechniać FUD. Jeden blok try / try nie zbliża się nawet do „złożonego”.
Tryptyk
47
Jednak to nie jest tak naprawdę FUD. Skutecznie piszesz 4 wiersze kodu, oczekując, że coś wybuchnie, wychwytujesz ten wyjątek i robisz swoje domyślne, zamiast korzystać z jednego linijki.
andersonvom
Odpowiedzi:
398
Jeśli naprawdę denerwujesz się używaniem try/excepts wszędzie, napisz po prostu funkcję pomocnika:
Więc pytoniczne jest rozwiązywanie prostych problemów ze złożonym mechanizmem? Istnieje algorytm wykrywania funkcji int zapisanej w funkcji „int” - nie rozumiem, dlaczego nie jest to ujawnione jako funkcja boolowska.
Aivar,
79
@Aivar: Ta 5-liniowa funkcja nie jest złożonym mechanizmem.
Tryptyk
34
Z wyjątkiem:>>> print RepresentsInt(10.0)True>>> print RepresentsInt(10.06)True
Dannid,
5
Myślę, że to „python” w tym sensie, że jeśli Python myśli, że ciąg jest liczbą całkowitą, to samo robi twój program. Jeśli Python się zmienia, zmienia się również twój program i bez zmiany jednego wiersza kodu. Jest w tym pewna wartość. W zależności od okoliczności może to być właściwe.
Shavais,
57
Nie wiem, dlaczego jest to akceptowana odpowiedź lub ma tak wiele pozytywnych opinii, ponieważ jest to dokładnie odwrotność tego, o co prosi OP.
FearlessFuture,
755
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.
to nie obsługuje „+17” bez dodatkowego specjalnego przypadku.
Bryan Oakley,
1
Musisz przetestować OBA OBIEKTY: lambda s: s.isdigit () lub (s.startswith ('-') is s [1:]. Isdigit ())
rob
4
@Roberto: oczywiście, że powinieneś! i jestem pewien, że jesteś w stanie to zrobić!
SilentGhost,
22
Uwaga: 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.
jfs
4
check_int('')podniesie wyjątek zamiast powrotuFalse
wordbug
97
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 =[# integers0,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:returnFalsereturnTruedef isInt_str(v):
v = str(v).strip()return v=='0'or(v if v.find('..')>-1else v.lstrip('-+').rstrip('0').rstrip('.')).isdigit()def isInt_re(v):import re
ifnot hasattr(isInt_re,'intRegex'):
isInt_re.intRegex = re.compile(r"^([+-]?[1-9]\d*|0)$")return isInt_re.intRegex.match(str(v).strip())isnotNonedef isInt_re2(v):return g_intRegex.match(str(v).strip())isnotNonedef 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))
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”.
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ę.
Być może część tego wynika z faktu, że liczba całkowita jest trochę arbitralna. System programowania nie może sobie pozwolić na luksus zakładania, że zawsze będzie reprezentacją dziesiętną. 0x4df, w niektórych miejscach jest poprawną liczbą całkowitą, aw innych 0891. Boję się pomyśleć, co może się pojawić, biorąc pod uwagę Unicode w tego rodzaju czekach.
PlexQ,
3
+1 za czas. Zgadzam się, że cały ten wyjątkowy biznes nie jest tak elegancki na tak proste pytanie. Spodziewałbyś się wbudowanej metody pomocniczej dla tak powszechnego problemu ...
RickyA
9
Wiem, że ten wątek jest w zasadzie uśpiony, ale +1 za rozważenie czasu wykonywania. Długość linii nie zawsze wskazuje na złożoność; i oczywiście try / try może wydawać się prosty (i łatwy do odczytu, co również jest ważne), ale jest to kosztowna operacja. Twierdziłbym, że hierarchia preferencji powinna zawsze wyglądać mniej więcej tak: 1. Łatwe do odczytania jawne rozwiązanie (SilentGhost). 2. Łatwe do odczytania ukryte rozwiązanie (tryptyk). 3. Nie ma trzech.
Eric Humphrey,
1
Dziękuję za wasze szczegółowe badania dotyczące tak pozornie nieistotnego tematu. Pójdę z isInt_str (), pythonic lub nie. Dokucza mi to, że nie znalazłem nic na temat znaczenia v.find („..”). Czy jest to jakaś specjalna składnia find lub przypadek na krawędzi łańcucha liczbowego?
JackLeEmmerdeur,
3
Tak, nieco przestarzała, ale wciąż bardzo miła i trafna analiza. W Pythonie 3.5 tryjest bardziej wydajny: isInt_try: 0.6552 / isInt_str: 0.6396 / isInt_re: 1.0296 / isInt_re2: 0.5168.
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
@BuzzMoschetti masz rację. Szybkim sposobem na naprawę jest usunięcie znaku minus przez re.replace (regex_search, regex_replace, content) przed zastosowaniem str.isdigit ()
Catbuilts
27
Użyj wyrażenia regularnego:
import re
defRepresentsInt(s):return re.match(r"[-+]?\d+$", s)isnotNone
+1: ujawnia, że jest to przerażająco skomplikowane i kosztowne w porównaniu z próbą / wyjątkiem.
S.Lott,
2
Wydaje mi się, że jest to w zasadzie wolniejsza, niestandardowa wersja „numerycznego” rozwiązania oferowanego przez @SilentGhost.
Greg,
@Greg: Ponieważ @SilentGhost nie zakrywa poprawnie znaków, ta wersja faktycznie działa.
S.Lott,
1
@ S.Lott: z pewnością każdy, kto umie publikować w SO, byłby w stanie rozszerzyć mój przykład na znaki.
SilentGhost,
2
Wyrażenia regularne dotyczą najbardziej złożonej i niejasnej rzeczy, jaka istnieje, uważam, że powyższa prosta kontrola jest znacznie wyraźniejsza, nawet jeśli uważam, że nadal jest brzydka, jest brzydsza.
PlexQ,
18
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 objectdef isInteger(i):
i = str(i)return i=='0'or(i if i.find('..')>-1else i.lstrip('-+').rstrip('0').rstrip('.')).isdigit()# Uses re module for regexdef isIntegre(i):import re
ifnot 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))isnotNone# When executed directly run Unit Testsif __name__ =='__main__':for obj in[# integers0,1,-1,1.0,-1.0,'0','0.','0.0','1','-1','+1','1.0','-1.0','+1.0',# non-integers1.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 outputif 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.0is an integer is an integre
1is an integer is an integre
-1is an integer is an integre
1.0is an integer is an integre
-1.0is 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.1is NOT an integer is NOT an integre
-1.1is 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
Zgadzam się, że mój zestaw testów jest przesadny. Lubię udowodnić, że mój kod działa, gdy go piszę. Ale czy uważasz, że moja funkcja isInteger to przesada? Na pewno nie.
Bruno Bronosky,
1
Właśnie otrzymałem głos negatywny bez komentarzy. Co jest z ludźmi? Rozumiem, że milenialsi używają teraz „polubień” jako „potwierdzeń przeczytania”. Ale czy teraz używają znaczników „down” jako „nie metody, którą wybrałem”? Może nie zdają sobie sprawy, że odejmuje 2 punkty od WŁASNEJ reputacji, aby głosować w dół na odpowiedź. SO / SE robi to, aby zachęcić do głosowania w dół tylko z powodu dezinformacji, w takim przypadku mam nadzieję, że zostawisz komentarz .
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+$")defRepresentsInt(s):return INT_RE.match(str(s))isnotNone
Byłbym zainteresowany, dlaczego próbujesz uniknąć próby: z wyjątkiem?
Kwestia stylu. Wydaje mi się, że słowa „spróbuj / wyjdź” należy używać tylko z rzeczywistymi błędami, a nie z normalnym przebiegiem programu.
Adam Matan,
2
@Udi Pasmon: Python dość często używa try / try, z wyjątkiem „normalnego” przebiegu programu. Na przykład każdy iterator zatrzymuje się z podniesionym wyjątkiem.
S.Lott,
3
-1: Chociaż twoja wskazówka przy kompilacji wyrażenia regularnego jest słuszna, nie masz racji krytykując Grega pod innym względem: re.match pasuje do początku łańcucha, więc ^ we wzorcu jest w rzeczywistości zbędne. (Jest inaczej, gdy używasz re.search).
ThomasH
S.Lott - Czy jest to uważane za rozsądny przepływ w pythonie? Czym różni się to od innych języków? Być może warto osobne pytanie.
Adam Matan,
1
Częste użycie try / try w Pythonie zostało omówione tutaj na SO. Spróbuj wyszukać „[python] oprócz”
S.Lott,
4
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:
Nie działa dla różnych notacji naukowych i / lub wykładniczych (np. 1.2E3, 10 ^ 3 itd.) - oba zwrócą False. Nie sądzę, aby inne odpowiedzi również to uwzględniały, a nawet Python 3.8 ma niespójne opinie, ponieważ type(1E2)daje, <class 'float'>a type(10^2)daje<class 'int'> .
Pusty ciąg wejściowy daje wartość True.
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, Falsejeś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.
Spróbuj / wyjdź wygląda na chodzenie po czyimś trawniku (spróbuj), a jeśli / kiedy zauważą i się zdenerwują (wyjątek) przeprosisz (załatwisz wyjątek), podczas gdy mój 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.
Zgadnij co replacerobi? Ponadto, 5-2na przykład niepoprawnie to zaakceptuje .
Ry-
Zgłasza błąd indeksu, jeślis='-'
Anti Earth
s = '-'; s.replace ('-', '') .isdigit () -> False
Vladyslav Savchenko
2
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 notin numbers:returnFalsereturnTruedef isInt_Digit(v):
v = str(v).strip()return v.isdigit()
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.
set(input_string) == set(string.digits)jeśli pominąć '-+ 'na początku i .0, E-1na końcu.
jfs
1
Oto funkcja, która analizuje bez zgłaszania błędów. Obsługuje oczywiste przypadki powrotu Nonepo awarii (domyślnie obsługuje znaki do 2000 '- / +' w CPython!):
#!/usr/bin/env pythondef get_int(number):
splits = number.split('.')if len(splits)>2:# too many splitsreturnNoneif len(splits)==2and splits[1]:# handle decimal part recursively :-)if get_int(splits[1])!=0:returnNone
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)))
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 ValueErrorwyją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)returnFalse
Myślę, że pytanie dotyczy prędkości, ponieważ próba / wyjątek ma karę czasową:
dane testowe
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)
rozwiązanie numpy (działa tylko z tablicami i Unicode)
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
spróbuj / z wyjątkiem
def check_num(s):try:
int(s)returnTrueexcept:returnFalsedef 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.
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:
Odpowiedzi:
Jeśli naprawdę denerwujesz się używaniem
try/except
s wszędzie, napisz po prostu funkcję pomocnika: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.
źródło
>>> print RepresentsInt(10.0)
True
>>> print RepresentsInt(10.06)
True
z dodatnimi liczbami całkowitymi, których możesz użyć
.isdigit
:nie działa jednak z ujemnymi liczbami całkowitymi. załóżmy, że możesz spróbować:
nie będzie działać z
'16.0'
formatem, który jest podobny doint
w tym sensie rzutowanie.edycja :
źródło
u'²'.isdigit()
jest prawdziwe, aleint(u'²')
podnosi wartość błędu. Użyju.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:
Oto wyniki porównania wydajności:
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):
Właśnie próbowałem dodać tę funkcję:
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ę.
źródło
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:
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:
źródło
Użyj wyrażenia regularnego:
Jeśli musisz zaakceptować ułamki dziesiętne:
Aby zwiększyć wydajność, jeśli robisz to często, skompiluj wyrażenie regularne tylko raz
re.compile()
.źródło
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.
A dla mniej ryzykownych członków klasy oto wynik:
źródło
Więc twoją funkcją byłoby:
źródło
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:
Byłbym zainteresowany, dlaczego próbujesz uniknąć próby: z wyjątkiem?
źródło
Muszę to robić cały czas i mam łagodną, ale niewątpliwie irracjonalną niechęć do używania schematu try / wyjątkiem. Używam tego:
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:
Możesz także przekazać x do str (), jeśli nie jesteś pewien, czy dane wejściowe są ciągiem znaków:
Istnieją co najmniej dwa przypadki (krawędzi?), W których się to rozpada:
type(1E2)
daje,<class 'float'>
atype(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.
źródło
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ę
lepiej byłoby przepisać na:
ponieważ s [1:] tworzy również nowy ciąg
Ale jest o wiele lepsze rozwiązanie
źródło
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 ()):
i znacząco konsekwentnie bije czasy reszty:
używając normalnego pytona 2.7:
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?
źródło
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.
źródło
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!):Niektóre testy:
Wyniki:
Do swoich potrzeb możesz użyć:
źródło
Proponuję następujące:
Z dokumentów :
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:¯ \ _ (ツ) _ / ¯
źródło
Mam jedną możliwość, która w ogóle nie używa int i nie powinna zgłaszać wyjątku, chyba że ciąg nie reprezentuje liczby
Powinien działać dla każdego rodzaju łańcucha, który przyjmuje zmienną, dodatnią, ujemną notację inżynierską ...
źródło
Myślę, że pytanie dotyczy prędkości, ponieważ próba / wyjątek ma karę czasową:
dane testowe
Najpierw utworzyłem listę 200 ciągów, 100 niedziałających i 100 ciągów numerycznych.
rozwiązanie numpy (działa tylko z tablicami i Unicode)
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.spróbuj / z wyjątkiem
Wydaje się, że numpy rozwiązanie jest znacznie szybsze.
źródło
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'
):źródło
int()
.Uh .. Spróbuj tego:
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:
Wywołanie a.isdigit () zwróci True.
źródło
2
przypisanej wartościa
.