Wie kann ich überprüfen, ob eine Zeichenfolge ein int darstellt, ohne try / exception zu verwenden?

466

Gibt es eine Möglichkeit festzustellen, ob eine Zeichenfolge eine Ganzzahl darstellt (z. B. '3', '-17'aber nicht '3.14'oder 'asfasfas'), ohne einen try / exception-Mechanismus zu verwenden?

is_int('3.14') = False
is_int('-7')   = True
Adam Matan
quelle
23
Warum versuchen beide, dies "auf die harte Tour" zu tun? Was ist los mit versuchen / außer?
S.Lott
5
Ja, was ist los mit try / außer? Besser um Vergebung bitten als um Erlaubnis.
mk12
53
Ich würde fragen, warum sollte diese einfache Sache versuchen / außer erfordern? Das Ausnahmesystem ist ein komplexes Tier, aber dies ist ein einfaches Problem.
Aivar
13
@ Aivar hör auf, FUD zu verbreiten. Ein einzelner Try / Except-Block nähert sich nicht einmal "komplex".
Triptychon
47
Es ist jedoch nicht wirklich FUD. Sie würden effektiv 4 Codezeilen schreiben, erwarten, dass etwas explodiert, diese Ausnahme abfangen und Ihre Standardeinstellung ausführen, anstatt einen Einzeiler zu verwenden.
andersonvom

Antworten:

398

Wenn Sie sich wirklich nur darüber ärgern, try/excepts überall zu verwenden, schreiben Sie einfach eine Hilfsfunktion:

def RepresentsInt(s):
    try: 
        int(s)
        return True
    except ValueError:
        return False

>>> print RepresentsInt("+123")
True
>>> print RepresentsInt("10.0")
False

Es wird viel mehr Code geben, um alle Zeichenfolgen, die Python als Ganzzahlen betrachtet, genau abzudecken. Ich sage, sei in diesem Fall einfach pythonisch.

Triptychon
quelle
124
Es ist also pythonisch, ein einfaches Problem mit einem komplexen Mechanismus zu lösen? Es gibt einen Algorithmus zum Erkennen der in int geschriebenen Funktion "int" von int - ich verstehe nicht, warum dies nicht als boolesche Funktion verfügbar gemacht wird.
Aivar
79
@Aivar: Diese 5-Zeilen-Funktion ist kein komplexer Mechanismus.
Triptychon
34
Außer:>>> print RepresentsInt(10.0) True >>> print RepresentsInt(10.06) True
Dannid
5
Ich denke, es ist "pythonisch" in dem Sinne, dass, wenn Python denkt, dass der String ein int ist, Ihr Programm dies auch tut. Wenn sich Python ändert, ändert sich auch Ihr Programm, ohne eine einzige Codezeile zu ändern. Das hat einen gewissen Wert. Abhängig von den Umständen kann dies das Richtige sein.
Shavais
57
Ich weiß nicht, warum dies die akzeptierte Antwort ist oder so viele positive Stimmen hat, da dies genau das Gegenteil von dem ist, was OP verlangt.
FearlessFuture
755

mit positiven ganzen Zahlen könnten Sie verwenden .isdigit:

>>> '16'.isdigit()
True

Es funktioniert jedoch nicht mit negativen ganzen Zahlen. Angenommen, Sie könnten Folgendes versuchen:

>>> s = '-17'
>>> s.startswith('-') and s[1:].isdigit()
True

Es funktioniert nicht mit dem '16.0'Format, das dem intCasting in diesem Sinne ähnlich ist .

bearbeiten :

def check_int(s):
    if s[0] in ('-', '+'):
        return s[1:].isdigit()
    return s.isdigit()
SilentGhost
quelle
6
Dies behandelt "+17" nicht ohne einen zusätzlichen Sonderfall.
Bryan Oakley
1
Sie müssen auf BEIDE Fälle testen: Lambda s: s.isdigit () oder (s.startswith ('-') und s [1:]. Isdigit ())
rob
4
@ Roberto: natürlich solltest du! und ich bin sicher, dass Sie dazu in der Lage sind!
SilentGhost
22
Hinweis: u'²'.isdigit()ist wahr, int(u'²')löst aber ValueError aus. Verwenden Sie u.isdecimal()stattdessen. str.isdigit()ist vom Gebietsschema abhängig von Python 2.
jfs
4
check_int('')wird eine Ausnahme False
auslösen,
97

Weißt du, ich habe festgestellt (und ich habe dies immer wieder getestet), dass try / without aus irgendeinem Grund nicht so gut funktioniert. Ich versuche häufig verschiedene Methoden, um Dinge zu tun, und ich glaube nicht, dass ich jemals eine Methode gefunden habe, die try / außer verwendet, um die besten der getesteten zu erzielen. Tatsächlich scheint es mir, dass diese Methoden normalerweise in der Nähe der am schlimmsten, wenn nicht am schlimmsten. Nicht in jedem Fall, aber in vielen Fällen. Ich weiß, dass viele Leute sagen, es sei der "pythonische" Weg, aber das ist ein Bereich, in dem ich mich von ihnen trenne. Für mich ist es weder sehr performant noch sehr elegant, daher verwende ich es eher nur zum Abfangen und Melden von Fehlern.

Ich wollte mich darüber beschweren, dass PHP, Perl, Ruby, C und sogar die verdammte Shell einfache Funktionen zum Testen eines Strings auf Integer-Hood haben, aber die gebotene Sorgfalt bei der Überprüfung dieser Annahmen hat mich gestolpert! Anscheinend ist dieser Mangel eine häufige Krankheit.

Hier ist eine schnelle und schmutzige Bearbeitung von Brunos Beitrag:

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

Hier sind die Ergebnisse des Leistungsvergleichs:

timings..
isInt_try:   0.6426
isInt_str:   0.7382
isInt_re:    1.1156
isInt_re2:   0.5344
check_int:   0.3452

Die AC-Methode könnte es einmal durch scannen und fertig sein. Eine AC-Methode, die den String einmal durchsucht, wäre meiner Meinung nach das Richtige.

BEARBEITEN:

Ich habe den obigen Code so aktualisiert, dass er in Python 3.5 funktioniert, die Funktion check_int aus der aktuell am häufigsten bewerteten Antwort enthält und den derzeit beliebtesten regulären Ausdruck verwendet, den ich zum Testen auf Integer-Hood finden kann. Diese Regex lehnt Zeichenfolgen wie 'abc 123' ab. Ich habe 'abc 123' als Testwert hinzugefügt.

Es ist sehr interessant für mich, an dieser Stelle zu bemerken, dass KEINE der getesteten Funktionen, einschließlich der try-Methode, der beliebten check_int-Funktion und des beliebtesten regulären Ausdrucks zum Testen auf Integer-Hood, die richtigen Antworten für alle zurückgibt Testwerte (je nachdem, was Sie für die richtigen Antworten halten; siehe die Testergebnisse unten).

Die integrierte Funktion int () schneidet den Bruchteil einer Gleitkommazahl stillschweigend ab und gibt den ganzzahligen Teil vor der Dezimalstelle zurück, es sei denn, die Gleitkommazahl wird zuerst in eine Zeichenfolge konvertiert.

Die Funktion check_int () gibt false für Werte wie 0.0 und 1.0 (die technisch gesehen ganze Zahlen sind) und true für Werte wie '06' zurück.

Hier sind die aktuellen (Python 3.5) Testergebnisse:

                  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   |

Gerade habe ich versucht, diese Funktion hinzuzufügen:

def isInt_float(s):
    try:
        return float(str(s)).is_integer()
    except:
        return False

Es funktioniert fast genauso gut wie check_int (0.3486) und gibt true für Werte wie 1.0 und 0.0 und +1.0 und 0. und .0 usw. zurück. Aber es gibt auch wahr für '06' zurück, also. Such dir dein Gift aus, denke ich.

Shavais
quelle
Vielleicht liegt ein Teil davon daran, dass eine ganze Zahl selbst ein bisschen willkürlich ist. Ein Programmiersystem kann nicht den Luxus annehmen, dass es immer eine Dezimaldarstellung sein wird. 0x4df ist an einigen Stellen eine gültige Ganzzahl, an anderen nicht 0891. Ich fürchte mich zu überlegen, was bei solchen Überprüfungen unter Unicode entstehen könnte.
PlexQ
3
+1 für das Timing. Ich stimme zu, dass dieses ganze Ausnahmegeschäft für eine so einfache Frage nicht wirklich elegant ist. Sie würden eine
eingebaute Hilfsmethode
9
Ich weiß, dass dieser Thread im Grunde genommen inaktiv ist, aber +1 für die Berücksichtigung der Laufzeit. Die Linienlänge zeigt nicht immer die zugrunde liegende Komplexität an. und sicher, ein Versuch / eine Ausnahme mag einfach aussehen (und einfach zu lesen, was ebenfalls wichtig ist), aber es ist eine kostspielige Operation. Ich würde argumentieren, dass die Präferenzhierarchie immer ungefähr so ​​aussehen sollte: 1. Eine einfach zu lesende explizite Lösung (SilentGhost's). 2. Eine einfach zu lesende implizite Lösung (Triptychons). 3. Es gibt keine drei.
Eric Humphrey
1
Vielen Dank für Ihre gründlichen Untersuchungen zu einem so scheinbar unbedeutenden Thema. Ich werde mit dem isInt_str () gehen, pythonisch oder nicht. Was mich nervt ist, dass ich nichts über die Bedeutung von v.find ('..') gefunden habe. Ist das eine spezielle Suchsyntax oder ein Randfall einer numerischen Zeichenfolge?
JackLeEmmerdeur
3
Ja, ein bisschen veraltet, aber immer noch sehr schöne und relevante Analyse. In Python 3.5 tryist effizienter: isInt_try: 0.6552 / isInt_str: 0.6396 / isInt_re: 1.0296 / isInt_re2: 0.5168.
Dave
39

str.isdigit() sollte den Trick machen.

Beispiele:

str.isdigit("23") ## True
str.isdigit("abc") ## False
str.isdigit("23.4") ## False

EDIT : Wie @BuzzMoschetti wies darauf hin, auf diese Weise wird für die fehler minus Zahl (zB „-23“ ). Wenn Ihre input_num kleiner als 0 sein kann, verwenden Sie re.sub (regex_search, regex_replace, content), bevor Sie str.isdigit () anwenden . Zum Beispiel:

import re
input_num = "-23"
input_num = re.sub("^-", "", input_num) ## "^" indicates to remove the first "-" only
str.isdigit(input_num) ## True
Catbuilts
quelle
1
Weil -23 falsch ergibt.
Buzz Moschetti
1
@BuzzMoschetti du hast recht. Eine schnelle Möglichkeit zur Behebung besteht darin, das Minuszeichen durch Ersetzen (regex_search, regex_replace, Inhalt) zu entfernen, bevor str.isdigit ()
Catbuilts angewendet wird
27

Verwenden Sie einen regulären Ausdruck:

import re
def RepresentsInt(s):
    return re.match(r"[-+]?\d+$", s) is not None

Wenn Sie auch Dezimalbrüche akzeptieren müssen:

def RepresentsInt(s):
    return re.match(r"[-+]?\d+(\.0*)?$", s) is not None

Wenn Sie dies häufig tun, kompilieren Sie den regulären Ausdruck nur einmal, um die Leistung zu verbessern re.compile().

Greg Hewgill
quelle
19
+1: zeigt, dass dies im Vergleich zu try / Except schrecklich komplex und teuer ist.
S.Lott
2
Ich bin der Meinung, dass dies im Wesentlichen eine langsamere, benutzerdefinierte Version der von @SilentGhost angebotenen "isnumerischen" Lösung ist.
Greg
@Greg: Da der @SilentGhost die Zeichen nicht richtig abdeckt, funktioniert diese Version tatsächlich.
S.Lott
1
@ S.Lott: Sicherlich könnte jeder, der in der Lage ist, auf SO zu posten, mein Beispiel auf Schilder ausweiten.
SilentGhost
2
Bei regulären Ausdrücken handelt es sich um die komplexeste und dunkelste Sache, die es gibt. Ich finde, dass die obige einfache Überprüfung wesentlich klarer ist, auch wenn ich denke, dass sie immer noch hässlich ist, ist dies hässlicher.
PlexQ
18

Die richtige RegEx-Lösung würde die Ideen von Greg Hewgill und Nowell kombinieren, aber keine globale Variable verwenden. Sie können dies erreichen, indem Sie der Methode ein Attribut hinzufügen. Ich weiß auch, dass es verpönt ist, Importe in eine Methode einzufügen, aber ich strebe einen "Lazy Module" -Effekt wie http://peak.telecommunity.com/DevCenter/Importing#lazy-imports an

edit: Meine bisherige Lieblingstechnik besteht darin, ausschließlich Methoden des String-Objekts zu verwenden.

#!/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))

Und für die weniger abenteuerlustigen Mitglieder der Klasse ist hier die Ausgabe:

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
Bruno Bronosky
quelle
4
Ich werde zustimmen, dass meine Testsuite übertrieben ist. Ich möchte beweisen, dass mein Code funktioniert, wenn ich ihn schreibe. Aber denkst du, meine isInteger-Funktion ist übertrieben? Sicher nicht.
Bruno Bronosky
1
Ich habe gerade eine Abstimmung ohne Kommentare erhalten. Was ist mit Menschen? Ich verstehe, dass Millennials jetzt "Likes" als "Lesebestätigungen" verwenden. Aber verwenden sie jetzt Abstimmungen als "nicht die Methode, die ich gewählt habe" -Marker? Vielleicht merken sie nicht, dass es 2 Punkte von IHREM EIGENEN Ruf abzieht , eine Antwort herunterzustimmen. SO / SE tut dies, um Abstimmungen nur aufgrund von Fehlinformationen zu fördern. In diesem Fall würde ich hoffen, dass Sie einen Kommentar hinterlassen .
Bruno Bronosky
5
>>> "+7".lstrip("-+").isdigit()
True
>>> "-7".lstrip("-+").isdigit()
True
>>> "7".lstrip("-+").isdigit()
True
>>> "13.4".lstrip("-+").isdigit()
False

Ihre Funktion wäre also:

def is_int(val):
   return val[1].isdigit() and val.lstrip("-+").isdigit()
alkos333
quelle
1
is_int ("2") löst IndexError aus.
Anttikoo
4

Greg Hewgills Ansatz fehlten einige Komponenten: das führende "^", das nur zum Anfang des Strings passte, und das Kompilieren des Re im Voraus. Mit diesem Ansatz können Sie jedoch einen Versuch vermeiden: außer:

import re
INT_RE = re.compile(r"^[-]?\d+$")
def RepresentsInt(s):
    return INT_RE.match(str(s)) is not None

Es würde mich interessieren, warum Sie versuchen, einen Versuch zu vermeiden: außer?

Nicht gut
quelle
1
Eine Frage des Stils. Ich denke, dass "try / Except" nur mit tatsächlichen Fehlern verwendet werden sollte, nicht mit normalem Programmablauf.
Adam Matan
2
@Udi Pasmon: Python nutzt try / mit Ausnahme des "normalen" Programmflusses ziemlich stark. Beispielsweise stoppt jeder Iterator mit einer ausgelösten Ausnahme.
S.Lott
3
-1: Obwohl Ihr Hinweis auf das Kompilieren des regulären Ausdrucks richtig ist, sind Sie falsch darin, Greg in anderer Hinsicht zu kritisieren: re.match stimmt mit dem Anfang des Strings überein , sodass das ^ im Muster tatsächlich redundant ist. (Dies ist anders, wenn Sie re.search verwenden).
ThomasH
S.Lott - Wird dies als angemessener Fluss in Python angesehen? Wie unterscheidet sich das von anderen Sprachen? Vielleicht ist es eine separate Frage wert.
Adam Matan
1
Pythons starker Einsatz von try / without wurde hier auf SO behandelt. Versuchen Sie eine Suche nach '[Python] außer'
S.Lott
4

Ich muss das die ganze Zeit tun, und ich habe eine milde, aber zugegebenermaßen irrationale Abneigung gegen die Verwendung des Try / Except-Musters. Ich benutze das:

all([xi in '1234567890' for xi in x])

Es werden keine negativen Zahlen berücksichtigt, daher können Sie ein Minuszeichen (falls vorhanden) entfernen und dann prüfen, ob das Ergebnis Ziffern von 0 bis 9 enthält:

all([xi in '1234567890' for xi in x.replace('-', '', 1)])

Sie können x auch an str () übergeben, wenn Sie nicht sicher sind, ob die Eingabe eine Zeichenfolge ist:

all([xi in '1234567890' for xi in str(x).replace('-', '', 1)])

Es gibt mindestens zwei (Rand-?) Fälle, in denen dies auseinander fällt:

  1. Es funktioniert nicht für verschiedene wissenschaftliche und / oder exponentielle Notationen (z. B. 1.2E3, 10 ^ 3 usw.) - beide geben False zurück. Ich glaube auch nicht, dass andere Antworten dies zuließen, und selbst Python 3.8 hat inkonsistente Meinungen, da type(1E2)gibt, <class 'float'>während type(10^2)gibt <class 'int'>.
  2. Eine leere Zeichenfolgeneingabe ergibt True.

Es funktioniert also nicht für jede mögliche Eingabe, aber wenn Sie wissenschaftliche Notation, Exponentialnotation und leere Zeichenfolgen ausschließen können, ist es eine einzeilige OK-Prüfung, die zurückgibt, Falsewenn x keine Ganzzahl und Truex eine Ganzzahl ist.

Ich weiß nicht, ob es pythonisch ist, aber es ist eine Zeile und es ist relativ klar, was der Code tut.

mRotten
quelle
Versuchen / Ausnehmen scheint, als würde man auf dem Rasen einer Person gehen (versuchen), und wenn / wenn jemand es bemerkt und wütend wird (Ausnahme), entschuldigt man sich (behandelt die Ausnahme), während mein all(xi in '1234567890' for xi in x])Muster eher so aussieht, als würde man um Erlaubnis bitten, über den Rasen zu gehen. Ich bin nicht begeistert, ein Erlaubnisgeber zu sein, aber hier sind wir.
mRotten
3

Ich glaube

s.startswith('-') and s[1:].isdigit()

wäre besser umzuschreiben an:

s.replace('-', '').isdigit()

weil s [1:] auch einen neuen String erstellt

Aber viel bessere Lösung ist

s.lstrip('+-').isdigit()
Vladyslav Savchenko
quelle
3
Ratet mal, was replacemacht? Auch dies wird 5-2zum Beispiel falsch akzeptiert .
Ry-
Wirft einen IndexError, wenns='-'
Anti Earth
s = '-'; s.replace ('-', '') .isdigit () -> False
Vladyslav Savchenko
2

Shavais 'Post hat mir sehr gut gefallen, aber ich habe noch einen Testfall hinzugefügt (und die eingebaute Funktion 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()

und es schlägt deutlich die Zeiten der anderen:

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

mit normaler 2.7 Python:

$ python --version
Python 2.7.10

Die beiden von mir hinzugefügten Testfälle (isInt_loop und isInt_digit) bestehen genau die gleichen Testfälle (beide akzeptieren nur vorzeichenlose Ganzzahlen), aber ich dachte, die Leute könnten klüger sein, wenn sie die String-Implementierung (isInt_loop) ändern, im Gegensatz zum eingebauten isdigit () Funktion, also habe ich es aufgenommen, obwohl es einen kleinen Unterschied in der Ausführungszeit gibt. (und beide Methoden schlagen alles andere um ein Vielfaches, aber behandeln Sie nicht die zusätzlichen Dinge: "./+/-")

Außerdem fand ich es interessant festzustellen, dass der Regex (isInt_re2-Methode) den String-Vergleich im selben Test, den Shavais 2012 (derzeit 2018) durchgeführt hat, übertroffen hat. Vielleicht wurden die Regex-Bibliotheken verbessert?

brw59
quelle
1

Dies ist meiner Meinung nach wahrscheinlich der einfachste und pythonischste Weg, sich dem anzunähern. Ich habe diese Lösung nicht gesehen und sie ist im Grunde die gleiche wie die Regex, aber ohne die Regex.

def is_int(test):
    import string
    return not (set(test) - set(string.digits))
Xenlyte
quelle
set(input_string) == set(string.digits)wenn wir überspringen '-+ 'am Anfang und .0, E-1am Ende.
JFS
1

Hier ist eine Funktion, die analysiert, ohne Fehler auszulösen. Es behandelt offensichtliche Fälle, in denen Noneein Fehler auftritt (behandelt standardmäßig bis zu 2000 '- / +' Zeichen in 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)

Einige Tests:

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

Ergebnisse:

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

Für Ihre Bedürfnisse können Sie verwenden:

def int_predicate(number):
     return get_int(number) is not None
Reut Sharabani
quelle
1

Ich schlage folgendes vor:

import ast

def is_int(s):
    return isinstance(ast.literal_eval(s), int)

Aus den Dokumenten :

Bewerten Sie sicher einen Ausdrucksknoten oder eine Zeichenfolge, die ein Python-Literal oder eine Containeranzeige enthält. Die bereitgestellte Zeichenfolge oder der bereitgestellte Knoten darf nur aus den folgenden Python-Literalstrukturen bestehen: Zeichenfolgen, Bytes, Zahlen, Tupel, Listen, Diktate, Mengen, Boolesche Werte und Keine.

Ich sollte beachten, dass dies eine ValueErrorAusnahme auslöst, wenn es gegen etwas aufgerufen wird, das kein Python-Literal darstellt. Da die Frage nach einer Lösung ohne Versuch / Ausnahme gestellt wurde, habe ich eine Lösung vom Typ Kobayashi-Maru dafür:

from ast import literal_eval
from contextlib import suppress

def is_int(s):
    with suppress(ValueError):
        return isinstance(literal_eval(s), int)
    return False

¯ \ _ (ツ) _ / ¯

Jesko Hüttenhain
quelle
0

Ich habe eine Möglichkeit, die int überhaupt nicht verwendet und keine Ausnahme auslösen sollte, es sei denn, die Zeichenfolge repräsentiert keine Zahl

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

Es sollte für jede Art von Zeichenfolge funktionieren, die float akzeptiert, positive, negative, technische Notation ...

Agomcas
quelle
0

Ich denke, die Frage hängt mit der Geschwindigkeit zusammen, da der Versuch / außer eine Zeitstrafe hat:

 Testdaten

Zuerst habe ich eine Liste mit 200 Zeichenfolgen, 100 fehlerhaften Zeichenfolgen und 100 numerischen Zeichenfolgen erstellt.

from random import shuffle
numbers = [u'+1'] * 100
nonumbers = [u'1abc'] * 100
testlist = numbers + nonumbers
shuffle(testlist)
testlist = np.array(testlist)

 Numpy-Lösung (funktioniert nur mit Arrays und Unicode)

np.core.defchararray.isnumeric kann auch mit Unicode-Zeichenfolgen arbeiten np.core.defchararray.isnumeric(u'+12'), gibt jedoch ein Array zurück. Es ist also eine gute Lösung, wenn Sie Tausende von Konvertierungen durchführen müssen und fehlende oder nicht numerische Daten haben.

import numpy as np
%timeit np.core.defchararray.isnumeric(testlist)
10000 loops, best of 3: 27.9 µs per loop # 200 numbers per loop

versuche / außer

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

Scheint, dass numpy Lösung viel schneller ist.

Carlos Vega
quelle
0

Wenn Sie nur niedrigere ASCII-Ziffern akzeptieren möchten, finden Sie hier folgende Tests:

Python 3.7+: (u.isdecimal() and u.isascii())

Python <= 3.6: (u.isdecimal() and u == str(int(u)))

Andere Antworten schlagen vor, .isdigit()oder zu verwenden, .isdecimal()aber beide enthalten einige Unicode-Zeichen wie '٢'( u'\u0662'):

u = u'\u0662'     # '٢'
u.isdigit()       # True
u.isdecimal()     # True
u.isascii()       # False (Python 3.7+ only)
u == str(int(u))  # False
krubo
quelle
Dies behandelt keine negativen Werte oder weiß gepolsterten Werte, die beide von gut verarbeitet werden int().
ShadowRanger
-6

Äh .. Versuchen Sie dies:

def int_check(a):
    if int(a) == a:
        return True
    else:
        return False

Dies funktioniert, wenn Sie keine Zeichenfolge eingeben, die keine Zahl ist.

Und außerdem (ich habe vergessen, den Teil zur Nummernprüfung einzufügen) gibt es eine Funktion, die prüft, ob die Zeichenfolge eine Zahl ist oder nicht. Es ist str.isdigit (). Hier ist ein Beispiel:

a = 2
a.isdigit()

Wenn Sie a.isdigit () aufrufen, wird True zurückgegeben.

HaulCozen
quelle
Ich denke, Sie brauchen Anführungszeichen um den 2zugewiesenen Wert a.
Luke Woodward
1
Warum ist diese Top-Antwort nicht? Es beantwortet die Frage genau.
Heuschrecke
6
-1 die Frage: "Überprüfen Sie, ob eine Zeichenfolge ein int darstellt, ohne Try / Except zu verwenden?" für @Caroline Alexiou
jfs