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?
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:
Es wird viel mehr Code geben, um alle Zeichenfolgen, die Python als Ganzzahlen betrachtet, genau abzudecken. Ich sage, sei in diesem Fall einfach pythonisch.
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.
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 .
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 =[# 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))
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:
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.
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.
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
@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
defRepresentsInt(s):return re.match(r"[-+]?\d+$", s)isnotNone
+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 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))
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.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
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 .
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+$")defRepresentsInt(s):return INT_RE.match(str(s))isnotNone
Es würde mich interessieren, warum Sie versuchen, einen Versuch zu vermeiden: außer?
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:
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'>.
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.
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.
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 notin numbers:returnFalsereturnTruedef isInt_Digit(v):
v = str(v).strip()return v.isdigit()
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?
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.
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 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)
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)))
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)returnFalse
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 ...
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)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
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.
Antworten:
Wenn Sie sich wirklich nur darüber ärgern,
try/except
s überall zu verwenden, schreiben Sie einfach eine Hilfsfunktion:Es wird viel mehr Code geben, um alle Zeichenfolgen, die Python als Ganzzahlen betrachtet, genau abzudecken. Ich sage, sei in diesem Fall einfach pythonisch.
quelle
>>> print RepresentsInt(10.0)
True
>>> print RepresentsInt(10.06)
True
mit positiven ganzen Zahlen könnten Sie verwenden
.isdigit
:Es funktioniert jedoch nicht mit negativen ganzen Zahlen. Angenommen, Sie könnten Folgendes versuchen:
Es funktioniert nicht mit dem
'16.0'
Format, das demint
Casting in diesem Sinne ähnlich ist .bearbeiten :
quelle
u'²'.isdigit()
ist wahr,int(u'²')
löst aber ValueError aus. Verwenden Sieu.isdecimal()
stattdessen.str.isdigit()
ist vom Gebietsschema abhängig von Python 2.check_int('')
wird eine AusnahmeFalse
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:
Hier sind die Ergebnisse des Leistungsvergleichs:
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:
Gerade habe ich versucht, diese Funktion hinzuzufügen:
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.
quelle
try
ist effizienter: isInt_try: 0.6552 / isInt_str: 0.6396 / isInt_re: 1.0296 / isInt_re2: 0.5168.str.isdigit()
sollte den Trick machen.Beispiele:
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:
quelle
Verwenden Sie einen regulären Ausdruck:
Wenn Sie auch Dezimalbrüche akzeptieren müssen:
Wenn Sie dies häufig tun, kompilieren Sie den regulären Ausdruck nur einmal, um die Leistung zu verbessern
re.compile()
.quelle
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.
Und für die weniger abenteuerlustigen Mitglieder der Klasse ist hier die Ausgabe:
quelle
Ihre Funktion wäre also:
quelle
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:
Es würde mich interessieren, warum Sie versuchen, einen Versuch zu vermeiden: außer?
quelle
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:
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:
Sie können x auch an str () übergeben, wenn Sie nicht sicher sind, ob die Eingabe eine Zeichenfolge ist:
Es gibt mindestens zwei (Rand-?) Fälle, in denen dies auseinander fällt:
type(1E2)
gibt,<class 'float'>
währendtype(10^2)
gibt<class 'int'>
.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,
False
wenn x keine Ganzzahl undTrue
x eine Ganzzahl ist.Ich weiß nicht, ob es pythonisch ist, aber es ist eine Zeile und es ist relativ klar, was der Code tut.
quelle
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.Ich glaube
wäre besser umzuschreiben an:
weil s [1:] auch einen neuen String erstellt
Aber viel bessere Lösung ist
quelle
replace
macht? Auch dies wird5-2
zum Beispiel falsch akzeptiert .s='-'
Shavais 'Post hat mir sehr gut gefallen, aber ich habe noch einen Testfall hinzugefügt (und die eingebaute Funktion isdigit ()):
und es schlägt deutlich die Zeiten der anderen:
mit normaler 2.7 Python:
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?
quelle
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.
quelle
set(input_string) == set(string.digits)
wenn wir überspringen'-+ '
am Anfang und.0
,E-1
am Ende.Hier ist eine Funktion, die analysiert, ohne Fehler auszulösen. Es behandelt offensichtliche Fälle, in denen
None
ein Fehler auftritt (behandelt standardmäßig bis zu 2000 '- / +' Zeichen in CPython!):Einige Tests:
Ergebnisse:
Für Ihre Bedürfnisse können Sie verwenden:
quelle
Ich schlage folgendes vor:
Aus den Dokumenten :
Ich sollte beachten, dass dies eine
ValueError
Ausnahme 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:¯ \ _ (ツ) _ / ¯
quelle
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
Es sollte für jede Art von Zeichenfolge funktionieren, die float akzeptiert, positive, negative, technische Notation ...
quelle
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.
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.versuche / außer
Scheint, dass numpy Lösung viel schneller ist.
quelle
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'
):quelle
int()
.Äh .. Versuchen Sie dies:
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:
Wenn Sie a.isdigit () aufrufen, wird True zurückgegeben.
quelle
2
zugewiesenen Werta
.