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?

is_int('3.14') = False
is_int('-7')   = True

23
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:

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.


124
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.

edycja :

def check_int(s):
    if s[0] in ('-', '+'):
        return s[1:].isdigit()
    return s.isdigit()

6
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 = [
    # 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ę.


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.
Dave

39

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

1
Ponieważ -23 daje fałsz.
Buzz Moschetti,

1
@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
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().


19
+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 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

4
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 .
Bruno Bronosky

5
>>> "+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()

1
is_int („2”) podnosi IndexError.
anttikoo

4

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?


1
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:

  1. 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'> .
  2. 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.
mRotten

3

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()

3
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 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?


1

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-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 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

1

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 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)
    return False

¯ \ _ (ツ) _ / ¯


0

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

float(number)==float(number)//1

Powinien działać dla każdego rodzaju łańcucha, który przyjmuje zmienną, dodatnią, ujemną notację inżynierską ...


0

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)
    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.


0

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

Nie będzie to obsługiwać wartości ujemnych ani wartości dopełnianych białymi znakami, z których obie są w porządku obsługiwane int().
ShadowRanger

-6

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.


Myślę, że potrzebujesz cudzysłowów wokół 2przypisanej wartości a.
Luke Woodward,

1
Dlaczego nie jest to najlepsza odpowiedź? Odpowiada dokładnie na pytanie.
konik polny

6
-1 pytanie: „Sprawdź, czy ciąg reprezentuje liczbę całkowitą, bez użycia Try / Except?” dla @Caroline Alexiou
jfs
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.