Frage Wie überprüfe ich, ob eine Zeichenfolge eine Zahl (Float) ist?


Was ist die beste Möglichkeit zu überprüfen, ob eine Zeichenfolge in Python als Zahl dargestellt werden kann?

Die Funktion, die ich momentan gerade habe, ist:

def is_number(s):
    try:
        float(s)
        return True
    except ValueError:
        return False

Was nicht nur hässlich und langsam ist, wirkt klobig. Allerdings habe ich keine bessere Methode gefunden, als anzurufen float In der Hauptfunktion ist es noch schlimmer.


1247
2017-12-09 20:03


Ursprung


Antworten:


Welches ist nicht nur hässlich und langsam

Ich würde beides bestreiten.

Eine Regex- oder andere String-Analyse wäre hässlicher und langsamer.

Ich bin mir nicht sicher, dass etwas viel schneller sein könnte als das obige. Es ruft die Funktion auf und kehrt zurück. Try / Catch verursacht keinen hohen Overhead, da die häufigste Ausnahme ohne umfangreiche Suche nach Stack-Frames abgefangen wird.

Das Problem ist, dass jede numerische Konvertierungsfunktion zwei Arten von Ergebnissen hat

  • Eine Nummer, wenn die Nummer gültig ist
  • Ein Statuscode (z. B. über errno) oder eine Ausnahme, um anzuzeigen, dass keine gültige Nummer analysiert werden konnte.

C (als Beispiel) hackt dies auf eine Anzahl von Wegen. Python legt es klar und deutlich dar.

Ich denke, dass dein Code dafür perfekt ist.


564
2017-12-09 20:30



Falls Sie nach Ganzzahlen (positive, nicht vorzeichenbehaftete) Ganzzahlen anstelle von Gleitkommazahlen suchen, können Sie die isdigit() Funktion für String-Objekte.

>>> a = "03523"
>>> a.isdigit()
True
>>> b = "963spam"
>>> b.isdigit()
False

String-Methoden - isdigit()

Es gibt auch etwas in Unicode-Strings, mit dem ich nicht so vertraut bin Unicode - ist dezimal / dezimal


1334
2017-12-09 20:15



Es gibt eine Ausnahme, die Sie berücksichtigen sollten: die Zeichenfolge 'NaN'

Wenn Sie wollen, dass is_number FALSE für 'NaN' zurückgibt, funktioniert dieser Code nicht, da Python ihn in seine Darstellung einer Zahl umwandelt, die keine Zahl ist (sprechen Sie über Identitätsprobleme):

>>> float('NaN')
nan

Ansonsten sollte ich Ihnen tatsächlich für den Code danken, den ich jetzt ausgiebig verwende. :)

G.


64
2017-09-01 14:06



TL; DR Die beste Lösung ist s.replace('.','',1).isdigit()

Ich habe etwas gemacht Benchmarks Vergleichen der verschiedenen Ansätze

def is_number_tryexcept(s):
    """ Returns True is string is a number. """
    try:
        float(s)
        return True
    except ValueError:
        return False

import re    
def is_number_regex(s):
    """ Returns True is string is a number. """
    if re.match("^\d+?\.\d+?$", s) is None:
        return s.isdigit()
    return True


def is_number_repl_isdigit(s):
    """ Returns True is string is a number. """
    return s.replace('.','',1).isdigit()

Wenn die Zeichenfolge keine Zahl ist, ist der Ausnahmeblock ziemlich langsam. Vor allem aber ist die try-except-Methode der einzige Ansatz, der wissenschaftliche Notationen korrekt behandelt.

funcs = [
          is_number_tryexcept, 
          is_number_regex,
          is_number_repl_isdigit
          ]

a_float = '.1234'

print('Float notation ".1234" is not supported by:')
for f in funcs:
    if not f(a_float):
        print('\t -', f.__name__)

Float Notation ".1234" wird nicht unterstützt von:
- is_number_regex

scientific1 = '1.000000e+50'
scientific2 = '1e50'


print('Scientific notation "1.000000e+50" is not supported by:')
for f in funcs:
    if not f(scientific1):
        print('\t -', f.__name__)




print('Scientific notation "1e50" is not supported by:')
for f in funcs:
    if not f(scientific2):
        print('\t -', f.__name__)

Wissenschaftliche Notation "1.000000e + 50" wird nicht unterstützt von:
- is_number_regex
- is_number_repl_isdigit
Wissenschaftliche Notation "1e50" wird nicht unterstützt von:
- is_number_regex
- is_number_repl_isdigit

EDIT: Die Benchmark-Ergebnisse

import timeit

test_cases = ['1.12345', '1.12.345', 'abc12345', '12345']
times_n = {f.__name__:[] for f in funcs}

for t in test_cases:
    for f in funcs:
        f = f.__name__
        times_n[f].append(min(timeit.Timer('%s(t)' %f, 
                      'from __main__ import %s, t' %f)
                              .repeat(repeat=3, number=1000000)))

wo die folgenden Funktionen getestet wurden

from re import match as re_match
from re import compile as re_compile

def is_number_tryexcept(s):
    """ Returns True is string is a number. """
    try:
        float(s)
        return True
    except ValueError:
        return False

def is_number_regex(s):
    """ Returns True is string is a number. """
    if re_match("^\d+?\.\d+?$", s) is None:
        return s.isdigit()
    return True


comp = re_compile("^\d+?\.\d+?$")    

def compiled_regex(s):
    """ Returns True is string is a number. """
    if comp.match(s) is None:
        return s.isdigit()
    return True


def is_number_repl_isdigit(s):
    """ Returns True is string is a number. """
    return s.replace('.','',1).isdigit()

enter image description here


64
2018-05-13 19:28



Wie wäre es damit:

'3.14'.replace('.','',1).isdigit()

was nur dann wahr ist, wenn es ein oder kein '.' in der Ziffernfolge.

'3.14.5'.replace('.','',1).isdigit()

wird falsch zurückgeben

edit: habe gerade einen weiteren Kommentar gesehen ... Hinzufügen eines .replace(badstuff,'',maxnum_badstuff) für andere Fälle kann getan werden. wenn Sie Salz und nicht willkürliche Gewürze passieren (Ref:xkcd # 974) Das wird gut gehen: P


52
2018-05-25 22:22



Aktualisiert, nachdem Alfe darauf hingewiesen hat, dass Sie nicht separat nach Float suchen müssen.

def is_number(s):
    try:
        complex(s) # for int, long, float and complex
    except ValueError:
        return False

    return True

Früher gesagt: In einigen seltenen Fällen müssen Sie möglicherweise auch nach komplexen Zahlen (z. B. 1 + 2i) suchen, die nicht durch ein Float dargestellt werden können:

def is_number(s):
    try:
        float(s) # for int, long and float
    except ValueError:
        try:
            complex(s) # for complex
        except ValueError:
            return False

    return True

38
2017-12-11 04:56



Was nicht nur hässlich und langsam ist, wirkt klobig.

Es kann etwas gewöhnungsbedürftig sein, aber das ist die pythonische Art, es zu tun. Wie bereits erwähnt, sind die Alternativen schlechter. Aber es gibt einen weiteren Vorteil, die Dinge auf diese Weise zu tun: Polymorphismus.

Die zentrale Idee hinter der Eingabe von Enten ist, dass "wenn es wie eine Ente geht und spricht, dann ist es eine Ente." Was ist, wenn Sie entscheiden, dass Sie einen String ableiten müssen, damit Sie ändern können, wie Sie bestimmen, ob etwas in einen Float umgewandelt werden kann? Oder was ist, wenn Sie sich entscheiden, ein anderes Objekt vollständig zu testen? Sie können diese Dinge tun, ohne den obigen Code ändern zu müssen.

Andere Sprachen lösen diese Probleme durch die Verwendung von Schnittstellen. Ich werde die Analyse speichern, welche Lösung besser für einen anderen Thread ist. Der Punkt ist jedoch, dass Python entschieden auf der Duck-Typ-Seite der Gleichung ist, und Sie werden sich wahrscheinlich an Syntax wie diese gewöhnen müssen, wenn Sie planen, viel in Python zu programmieren (aber das bedeutet nicht du musst es natürlich mögen).

Eine andere Sache, die Sie vielleicht in Betracht ziehen sollten: Python ist ziemlich schnell im Auswerfen und Abfangen von Ausnahmen im Vergleich zu vielen anderen Sprachen (30x schneller als. NET zum Beispiel). Ach ja, die Sprache selbst löst sogar Ausnahmen aus, um nicht-außergewöhnliche, normale Programmbedingungen zu kommunizieren (jedes Mal, wenn Sie eine for-Schleife verwenden). Daher mache ich mir keine Gedanken über die Leistungsaspekte dieses Codes, bis Sie ein signifikantes Problem bemerken.


37
2017-09-08 08:42



Zum int benutze das:

>>> "1221323".isdigit()
True

Aber für float wir brauchen ein paar Tricks ;-). Jede Float-Nummer hat einen Punkt ...

>>> "12.34".isdigit()
False
>>> "12.34".replace('.','',1).isdigit()
True
>>> "12.3.4".replace('.','',1).isdigit()
False

Auch für negative Zahlen einfach hinzufügen lstrip():

>>> '-12'.lstrip('-')
'12'

Und jetzt bekommen wir einen universellen Weg:

>>> '-12.34'.lstrip('-').replace('.','',1).isdigit()
True
>>> '.-234'.lstrip('-').replace('.','',1).isdigit()
False

18
2018-02-18 01:35



Einfach C # imitieren

In C # gibt es zwei verschiedene Funktionen, die das Parsen von Skalarwerten behandeln:

  • Float.Parse ()
  • Float.TryParse ()

float.parse ():

def parse(string):
    try:
        return float(string)
    except Exception:
        throw TypeError

Hinweis: Wenn Sie sich fragen, warum ich die Ausnahme in einen TypeError geändert habe, Hier ist die Dokumentation.

float.try_parse ():

def try_parse(string, fail=None):
    try:
        return float(string)
    except Exception:
        return fail;

Hinweis: Sie möchten den booleschen Wert 'Falsch' nicht zurückgeben, da dies immer noch ein Werttyp ist. Nichts ist besser, weil es einen Fehler anzeigt. Wenn Sie etwas anderes möchten, können Sie natürlich den Fehlerparameter auf das ändern, was Sie wollen.

Um Float auf 'parse ()' und 'try_parse ()' zu erweitern, müssen Sie die 'float' Klasse anpassen, um diese Methoden hinzuzufügen.

Wenn Sie vorbestehende Funktionen respektieren möchten, sollte der Code so aussehen:

def monkey_patch():
    if(!hasattr(float, 'parse')):
        float.parse = parse
    if(!hasattr(float, 'try_parse')):
        float.try_parse = try_parse

SideNote: Ich persönlich bevorzuge es, Monkey Punching zu nennen, weil es sich anfühlt, als würde ich die Sprache missbrauchen, wenn ich das mache, aber YMMV.

Verwendung:

float.parse('giggity') // throws TypeException
float.parse('54.3') // returns the scalar value 54.3
float.tryParse('twank') // returns None
float.tryParse('32.2') // returns the scalar value 32.2

Und die großen Weisen Pythonas sagten zum Heiligen Stuhl Sharpisus: "Alles, was du tun kannst, kann ich besser machen; ich kann alles besser als du."


14
2017-08-14 03:34



Für Strings von Nicht-Nummern, try: except: ist eigentlich langsamer als reguläre Ausdrücke. Bei Strings mit gültigen Zahlen ist Regex langsamer. Die geeignete Methode hängt also von Ihrer Eingabe ab.

Wenn Sie feststellen, dass Sie sich in einer Leistungsbindung befinden, können Sie ein neues Drittanbietermodul verwenden, das aufgerufen wird Fastnummern das bietet eine Funktion namens Isfloat. Vollständige Offenlegung, ich bin der Autor. Ich habe seine Ergebnisse zu den Zeitpunkten unten eingefügt.


from __future__ import print_function
import timeit

prep_base = '''\
x = 'invalid'
y = '5402'
z = '4.754e3'
'''

prep_try_method = '''\
def is_number_try(val):
    try:
        float(val)
        return True
    except ValueError:
        return False

'''

prep_re_method = '''\
import re
float_match = re.compile(r'[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?$').match
def is_number_re(val):
    return bool(float_match(val))

'''

fn_method = '''\
from fastnumbers import isfloat

'''

print('Try with non-number strings', timeit.timeit('is_number_try(x)',
    prep_base + prep_try_method), 'seconds')
print('Try with integer strings', timeit.timeit('is_number_try(y)',
    prep_base + prep_try_method), 'seconds')
print('Try with float strings', timeit.timeit('is_number_try(z)',
    prep_base + prep_try_method), 'seconds')
print()
print('Regex with non-number strings', timeit.timeit('is_number_re(x)',
    prep_base + prep_re_method), 'seconds')
print('Regex with integer strings', timeit.timeit('is_number_re(y)',
    prep_base + prep_re_method), 'seconds')
print('Regex with float strings', timeit.timeit('is_number_re(z)',
    prep_base + prep_re_method), 'seconds')
print()
print('fastnumbers with non-number strings', timeit.timeit('isfloat(x)',
    prep_base + 'from fastnumbers import isfloat'), 'seconds')
print('fastnumbers with integer strings', timeit.timeit('isfloat(y)',
    prep_base + 'from fastnumbers import isfloat'), 'seconds')
print('fastnumbers with float strings', timeit.timeit('isfloat(z)',
    prep_base + 'from fastnumbers import isfloat'), 'seconds')
print()

Try with non-number strings 2.39108395576 seconds
Try with integer strings 0.375686168671 seconds
Try with float strings 0.369210958481 seconds

Regex with non-number strings 0.748660802841 seconds
Regex with integer strings 1.02021503448 seconds
Regex with float strings 1.08564686775 seconds

fastnumbers with non-number strings 0.174362897873 seconds
fastnumbers with integer strings 0.179651021957 seconds
fastnumbers with float strings 0.20222902298 seconds

Wie du siehst

  • try: except: war schnell für die numerische Eingabe, aber sehr langsam für eine ungültige Eingabe
  • Regex ist sehr effizient, wenn die Eingabe ungültig ist
  • fastnumbers gewinnt in beiden Fällen

14
2018-01-05 15:21



Ich weiß, dass dies besonders alt ist, aber ich würde eine Antwort hinzufügen, die meiner Meinung nach die fehlenden Informationen aus der Antwort mit der höchsten Wahl abdeckt, die für jeden, der dies findet, sehr wertvoll sein könnte:

Für jede der folgenden Methoden verbinden Sie sie mit einer Zählung, wenn Sie eine Eingabe benötigen, um akzeptiert zu werden. (Angenommen, wir verwenden vokale Definitionen von Ganzzahlen anstelle von 0-255 usw.)

x.isdigit() funktioniert gut, um zu überprüfen, ob x eine Ganzzahl ist.

x.replace('-','').isdigit() funktioniert gut, um zu überprüfen, ob x negativ ist. (Check-in erste Position)

x.replace('.','').isdigit() funktioniert gut, um zu überprüfen, ob x eine Dezimalzahl ist.

x.replace(':','').isdigit() funktioniert gut, um zu überprüfen, ob x ein Verhältnis ist.

x.replace('/','',1).isdigit() funktioniert gut, um zu überprüfen, ob x ein Bruch ist.


13
2018-03-04 16:12