Pertanyaan Bagaimana cara memeriksa apakah string adalah angka (float)?


Apa cara terbaik untuk memeriksa apakah string dapat direpresentasikan sebagai angka dengan Python?

Fungsi yang saya miliki saat ini adalah:

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

Yang, tidak hanya jelek dan lambat, tampaknya kikuk. Namun saya belum menemukan metode yang lebih baik karena menelepon float dalam fungsi utama bahkan lebih buruk.


1247
2017-12-09 20:03


asal


Jawaban:


Yang, tidak hanya jelek dan lambat

Saya akan membantah keduanya.

Sebuah regex atau string parsing lainnya akan menjadi lebih jelek dan lebih lambat.

Saya tidak yakin apa pun bisa lebih cepat dari yang di atas. Ini memanggil fungsi dan mengembalikan. Try / Catch tidak memperkenalkan banyak overhead karena pengecualian yang paling umum ditangkap tanpa pencarian yang ekstensif dari stack frame.

Masalahnya adalah bahwa fungsi konversi numerik memiliki dua macam hasil

  • Nomor, jika nomor tersebut valid
  • Kode status (misalnya, via errno) atau pengecualian untuk menunjukkan bahwa tidak ada nomor yang valid yang dapat diuraikan.

C (sebagai contoh) meretas di sekitar ini dengan sejumlah cara. Python menjabarkannya dengan jelas dan eksplisit.

Saya pikir kode Anda untuk melakukan ini sempurna.


564
2017-12-09 20:30



Jika Anda mencari parsing (positif, unsigned) bilangan bulat bukannya mengapung, Anda dapat menggunakan isdigit() fungsi untuk objek string.

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

Metode String - isdigit()

Ada juga sesuatu di string Unicode, yang saya tidak terlalu kenal Unicode - Apakah desimal / desimal


1334
2017-12-09 20:15



Ada satu pengecualian yang mungkin ingin Anda perhitungkan: string 'NaN'

Jika Anda ingin is_number mengembalikan FALSE untuk 'NaN', kode ini tidak akan berfungsi karena Python mengubahnya menjadi representasi angka yang bukan angka (bicara tentang masalah identitas):

>>> float('NaN')
nan

Kalau tidak, saya harus benar-benar berterima kasih atas potongan kode yang sekarang saya gunakan secara ekstensif. :)

G.


64
2017-09-01 14:06



TL; DR Solusi terbaik adalah s.replace('.','',1).isdigit()

Saya melakukan beberapa tolok ukur membandingkan berbagai pendekatan

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

Jika string bukan angka, blok kecuali cukup lambat. Tetapi yang lebih penting, metode try-except adalah satu-satunya pendekatan yang menangani notasi ilmiah dengan benar.

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

Notasi apung ".1234" tidak didukung oleh:
- 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__)

Notasi ilmiah "1.000000e + 50" tidak didukung oleh:
- is_number_regex
- is_number_repl_isdigit
Notasi ilmiah "1e50" tidak didukung oleh:
- is_number_regex
- is_number_repl_isdigit

EDIT: Hasil patokan

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

di mana fungsi-fungsi berikut diuji

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



bagaimana dengan ini:

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

yang akan mengembalikan true hanya jika ada satu atau tidak '.' dalam string angka.

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

akan kembali salah

edit: lihat saja komentar lain ... menambahkan a .replace(badstuff,'',maxnum_badstuff) untuk kasus lain bisa dilakukan. jika Anda melewati garam dan bukan bumbu sembarangan (ref:xkcd # 974) ini akan baik-baik saja: P


52
2018-05-25 22:22



Diperbarui setelah Alfe menunjukkan Anda tidak perlu memeriksa float secara terpisah sebagai pegangan kompleks:

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

    return True

Sebelumnya berkata: Apakah beberapa kasus langka Anda mungkin juga perlu memeriksa bilangan kompleks (mis. 1 + 2i), yang tidak dapat diwakili oleh pelampung:

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



Yang, tidak hanya jelek dan lambat, tampaknya kikuk.

Mungkin perlu waktu untuk membiasakan diri, tetapi ini adalah cara pythonic untuk melakukannya. Seperti yang sudah disebutkan, alternatifnya lebih buruk. Tetapi ada satu keuntungan lain dari melakukan hal-hal seperti ini: polimorfisme.

Ide utama di balik mengetik bebek adalah bahwa "jika berjalan dan berbicara seperti bebek, maka itu adalah bebek." Bagaimana jika Anda memutuskan bahwa Anda perlu string subkelas sehingga Anda dapat mengubah cara Anda menentukan apakah sesuatu dapat diubah menjadi float? Atau bagaimana jika Anda memutuskan untuk menguji beberapa objek lain sepenuhnya? Anda dapat melakukan hal-hal ini tanpa harus mengubah kode di atas.

Bahasa lain memecahkan masalah ini dengan menggunakan antarmuka. Saya akan menyimpan analisis solusi mana yang lebih baik untuk utas lainnya. Intinya, meskipun, adalah bahwa python jelas pada sisi mengetik bebek dari persamaan, dan Anda mungkin akan harus terbiasa dengan sintaks seperti ini jika Anda berencana untuk melakukan banyak program dengan Python (tapi itu tidak berarti Anda harus menyukainya tentu saja).

Satu hal lain yang mungkin ingin Anda pertimbangkan: Python cukup cepat dalam melempar dan menangkap pengecualian dibandingkan dengan banyak bahasa lain (30x lebih cepat daripada .Net misalnya). Heck, bahasa itu sendiri bahkan melempar pengecualian untuk mengkomunikasikan kondisi program normal yang tidak biasa (setiap kali Anda menggunakan for loop). Jadi, saya tidak akan terlalu khawatir tentang aspek kinerja dari kode ini sampai Anda melihat masalah yang signifikan.


37
2017-09-08 08:42



Untuk int Gunakan ini:

>>> "1221323".isdigit()
True

Tapi untuk float kami membutuhkan beberapa trik ;-). Setiap nomor float memiliki satu poin ...

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

Juga untuk angka negatif tambahkan saja lstrip():

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

Dan sekarang kita mendapatkan cara universal:

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

18
2018-02-18 01:35



Cuma Mimik C #

Dalam C # ada dua fungsi berbeda yang menangani parsing nilai skalar:

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

float.parse ():

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

Catatan: Jika Anda bertanya-tanya mengapa saya mengubah pengecualian ke TypeError, ini dokumentasinya.

float.try_parse ():

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

Catatan: Anda tidak ingin mengembalikan boolean 'False' karena itu masih merupakan jenis nilai. Tidak ada yang lebih baik karena ini menunjukkan kegagalan. Tentu saja, jika Anda menginginkan sesuatu yang berbeda, Anda dapat mengubah parameter gagal menjadi apa pun yang Anda inginkan.

Untuk memperluas float untuk menyertakan 'parse ()' dan 'try_parse ()' Anda harus monkeypatch the 'float' class untuk menambahkan metode ini.

Jika Anda ingin menghargai fungsi yang sudah ada sebelumnya, kode seharusnya seperti:

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

SideNote: Saya pribadi lebih suka menyebutnya Monkey Punching karena rasanya saya menyalahgunakan bahasa ketika saya melakukan ini tetapi YMMV.

Pemakaian:

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

Dan Sage Pythonas yang agung berkata kepada Holy See Sharpisus, "Apa pun yang dapat Anda lakukan saya bisa melakukan lebih baik; saya bisa melakukan apa pun yang lebih baik dari Anda."


14
2017-08-14 03:34