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 memanggil float
fungsi utama bahkan lebih buruk.
python
casting
floating-point
type-conversion
Daniel Goldberg
sumber
sumber
x = float('0.00'); if x: use_float(x);
sekarang Anda punya bug dalam kode Anda. Nilai-nilai kebenaran adalah alasan mengapa fungsi-fungsi ini memunculkan pengecualian daripada mengembalikannyaNone
. Solusi yang lebih baik adalah dengan menghindari fungsi utilitas dan mengelilingi panggilan untuk mengapung ditry catch
saat Anda ingin menggunakannya.Jawaban:
Saya akan membantah keduanya.
Regex atau metode penguraian string lainnya akan lebih jelek dan lebih lambat.
Saya tidak yakin bahwa banyak hal bisa lebih cepat daripada yang di atas. Ini memanggil fungsi dan kembali. Try / Catch tidak memperkenalkan banyak overhead karena pengecualian yang paling umum ditangkap tanpa pencarian yang luas dari stack frame.
Masalahnya adalah fungsi konversi numerik apa pun memiliki dua jenis hasil
C (sebagai contoh) meretas ini dengan beberapa cara. Python menjabarkannya dengan jelas dan eksplisit.
Saya pikir kode Anda untuk melakukan ini sempurna.
sumber
try
klausa, jadi saya akan memasukkannya kereturn True
dalamelse
klausatry
. Salah satu alasannya adalah bahwa dengan kode dalam pertanyaan, jika saya harus memeriksanya, saya harus memeriksa bahwa pernyataan kedua dalamtry
klausa tidak dapat meningkatkan ValueError: begitu saja, ini tidak memerlukan terlalu banyak waktu atau kekuatan otak, tetapi mengapa menggunakan apa pun saat tidak diperlukan?IsNumeric()
saya berakhir dengan mencoba / menangkap atau membungkus mencoba / menangkap. Ughif is_number(s): x = float(x) else: // fail
jumlah baris kode yang sama dengantry: x = float(x) catch TypeError: # fail
. Fungsi utilitas ini adalah abstraksi yang sama sekali tidak perlu.Jika Anda mencari parsing (positif, tidak bertanda) bilangan alih-alih mengapung, Anda dapat menggunakan
isdigit()
fungsi untuk objek string.Metode String -
isdigit()
: Python2 , Python3Ada juga sesuatu pada string Unicode, yang saya tidak terlalu kenal dengan Unicode - Desimal / desimal
sumber
isdigit()
danint()
memiliki pendapat yang berbeda tentang apa yang adalah bilangan bulat misalnya, untuk karakter Unicodeu'\u00b9'
:u'¹'.isdigit()
adalahTrue
tetapiint(u'¹')
menimbulkan ValueError.TL; DR Solusi terbaik adalah
s.replace('.','',1).isdigit()
Saya melakukan beberapa tolok ukur membandingkan berbagai pendekatan
Jika string bukan angka, blok kecuali-cukup lambat. Tetapi yang lebih penting, metode coba-kecuali adalah satu-satunya pendekatan yang menangani notasi ilmiah dengan benar.
Notasi float ".1234" tidak didukung oleh:
- is_number_regex
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 benchmark
di mana fungsi-fungsi berikut diuji
sumber
s.replace('.','',1).isdigit()
) harus muncul di awal anwser ini. Bagaimanapun, itu harus yang diterima. Terima kasih!'1.5e-9'
atau negatif.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 mengonversinya menjadi representasi nomor yang bukan angka (bicara tentang masalah identitas):
Kalau tidak, saya harus benar-benar berterima kasih atas potongan kode yang sekarang saya gunakan secara luas. :)
G.
sumber
NaN
mungkin nilai yang baik untuk dikembalikan (bukanFalse
) jika teks yang dikirimkan sebenarnya bukan representasi dari angka. Memeriksanya adalah jenis rasa sakit (float
tipe Python benar-benar membutuhkan metode untuk itu) tetapi Anda dapat menggunakannya dalam perhitungan tanpa menghasilkan kesalahan, dan hanya perlu memeriksa hasilnya.'inf'
. Entahinf
atauNaN
bisa juga diawali dengan+
atau-
masih diterima.x-1 == x
benar untuk mengapung besar lebih kecil dariinf
. Dari Python 3.2 Anda dapat menggunakanmath.isfinite
untuk menguji angka yang bukan NaN atau tidak terbatas, atau periksa keduanyamath.isnan
danmath.isinf
sebelum itu.bagaimana dengan ini:
yang akan mengembalikan true hanya jika ada satu atau tidak ada '.' dalam deretan digit.
akan kembali salah
sunting: hanya melihat komentar lain ... menambahkan
.replace(badstuff,'',maxnum_badstuff)
untuk kasus lain dapat dilakukan. jika Anda mengeluarkan garam dan bukan bumbu sembarang (ref: xkcd # 974 ) ini akan baik-baik saja: Psumber
1.234e56
(yang mungkin juga ditulis sebagai+1.234E+56
dan beberapa varian lainnya).re.match(r'^[+-]*(0[xbo])?[0-9A-Fa-f]*\.?[0-9A-Fa-f]*(E[+-]*[0-9A-Fa-f]+)$', 'str')
harus melakukan pekerjaan yang lebih baik dalam menentukan nomor (tetapi tidak semua, saya tidak mengklaim itu). Saya tidak menyarankan menggunakan ini, lebih baik menggunakan kode asli si Penanya.Mungkin perlu beberapa waktu untuk membiasakan diri, tetapi ini adalah cara pythonic untuk melakukannya. Seperti yang telah ditunjukkan, alternatifnya lebih buruk. Tetapi ada satu keuntungan lain dari melakukan hal-hal seperti ini: polimorfisme.
Gagasan utama di balik mengetik bebek adalah bahwa "jika berjalan dan berbicara seperti bebek, maka itu bebek." Bagaimana jika Anda memutuskan bahwa Anda perlu subkelas string sehingga Anda dapat mengubah cara Anda menentukan apakah sesuatu dapat dikonversi menjadi float? Atau bagaimana jika Anda memutuskan untuk menguji beberapa objek lain seluruhnya? Anda dapat melakukan hal-hal ini tanpa harus mengubah kode di atas.
Bahasa lain menyelesaikan masalah ini dengan menggunakan antarmuka. Saya akan menyimpan analisis solusi mana yang lebih baik untuk utas lainnya. Intinya, bagaimanapun, adalah bahwa python sudah pasti di sisi mengetik bebek persamaan, dan Anda mungkin harus membiasakan diri dengan sintaksis seperti ini jika Anda berencana untuk melakukan banyak pemrograman dalam Python (tapi itu tidak berarti Anda harus menyukainya tentu saja).
Satu hal lagi yang perlu Anda pertimbangkan: Python cukup cepat dalam melempar dan menangkap pengecualian dibandingkan dengan banyak bahasa lain (30x lebih cepat dari .Net misalnya). Heck, bahasa itu sendiri bahkan melempar pengecualian untuk mengkomunikasikan kondisi program normal yang tidak biasa (setiap kali Anda menggunakan for for loop). Jadi, saya tidak akan terlalu khawatir tentang aspek kinerja kode ini sampai Anda melihat masalah yang signifikan.
sumber
hasattr()
mana hanya sebuahgetattr()
panggilan yang dibungkus dengan atry/except
. Namun, penanganan pengecualian lebih lambat daripada kontrol aliran normal, sehingga menggunakannya untuk sesuatu yang benar sebagian besar waktu dapat menghasilkan penalti kinerja.Diperbarui setelah Alfe menunjukkan Anda tidak perlu memeriksa float secara terpisah karena kompleks menangani keduanya:
Dikatakan sebelumnya: Apakah beberapa kasus langka Anda mungkin perlu memeriksa bilangan kompleks (misalnya 1 + 2i), yang tidak dapat diwakili oleh pelampung:
sumber
float()
barang sepenuhnya dan cukup memeriksacomplex()
panggilan untuk berhasil. Segala sesuatu yang diuraikanfloat()
dapat diuraikancomplex()
.complex('(01989)')
akan kembali(1989+0j)
. Tetapifloat('(01989)')
akan gagal. Jadi saya pikir menggunakancomplex
itu bukan ide yang baik.Untuk
int
menggunakan ini:Tetapi untuk
float
kita memerlukan beberapa trik ;-). Setiap angka float memiliki satu titik ...Juga untuk angka negatif tambahkan saja
lstrip()
:Dan sekarang kita mendapatkan cara universal:
sumber
1.234e56
dan sejenisnya. Juga, saya akan tertarik bagaimana Anda mengetahui bahwa99999999999999999999e99999999999999999999
itu bukan angka. Mencoba menguraikannya dengan cepat.Just Mimic C #
Di C # ada dua fungsi berbeda yang menangani parsing nilai skalar:
float.parse ():
Catatan: Jika Anda bertanya-tanya mengapa saya mengubah pengecualian menjadi TypeError, inilah dokumentasinya .
float.try_parse ():
Catatan: Anda tidak ingin mengembalikan boolean 'False' karena itu masih merupakan tipe 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 memperpanjang float untuk memasukkan 'parse ()' dan 'try_parse ()' Anda harus memonetipe kelas 'float' untuk menambahkan metode ini.
Jika Anda ingin menghormati fungsi yang sudah ada sebelumnya, kode harus berupa:
SideNote: Saya pribadi lebih suka menyebutnya Monkey Punching karena rasanya saya menyalahgunakan bahasa ketika saya melakukan ini tetapi YMMV.
Pemakaian:
Dan Sage Pythonas yang agung berkata kepada Takhta Suci, "Apa pun yang dapat Anda lakukan, saya bisa berbuat lebih baik; saya bisa melakukan sesuatu yang lebih baik daripada Anda."
sumber
!
bukannyanot
mungkin kesalahan kecil, tetapi Anda pasti tidak dapat menetapkan atribut ke built-infloat
di CPython.Untuk string bukan angka,
try: except:
sebenarnya lebih lambat dari ekspresi reguler. Untuk string angka yang valid, regex lebih lambat. Jadi, metode yang tepat tergantung pada input Anda.Jika Anda menemukan bahwa Anda berada dalam ikatan kinerja, Anda dapat menggunakan modul pihak ketiga baru yang disebut fastnumber yang menyediakan fungsi yang disebut isfloat . Pengungkapan penuh, akulah penulisnya. Saya telah memasukkan hasilnya dalam pengaturan waktu di bawah ini.
Seperti yang Anda lihat
try: except:
cepat untuk input numerik tetapi sangat lambat untuk input yang tidak validfastnumbers
menang dalam kedua kasussumber
prep_code_basis
danprep_code_re_method
akan mencegah kesalahan saya.isfloat
fungsinya?str(s).strip('-').replace('.','',1).isdigit()
adalah sekitar 10x lebih lambat!Saya tahu ini sudah sangat lama, tetapi saya akan menambahkan jawaban. Saya yakin ini mencakup informasi yang hilang dari jawaban pilihan tertinggi yang bisa sangat berharga bagi siapa pun yang menemukan ini:
Untuk masing-masing metode berikut sambungkan dengan hitungan jika Anda memerlukan input apa pun untuk diterima. (Dengan asumsi kita menggunakan definisi vokal bilangan bulat daripada 0-255, dll.)
x.isdigit()
berfungsi dengan baik untuk memeriksa apakah x adalah bilangan bulat.x.replace('-','').isdigit()
berfungsi dengan baik untuk memeriksa apakah x adalah negatif. (Periksa - di posisi pertama)x.replace('.','').isdigit()
bekerja dengan baik untuk memeriksa apakah x adalah desimal.x.replace(':','').isdigit()
berfungsi dengan baik untuk memeriksa apakah x adalah rasio.x.replace('/','',1).isdigit()
bekerja dengan baik untuk memeriksa apakah x adalah pecahan.sumber
x.replace('/','',1).isdigit()
atau tanggal seperti 4/7/2017 akan disalahartikan sebagai angka.Jawaban ini memberikan panduan langkah demi langkah yang berfungsi dengan contoh untuk menemukan string adalah:
Periksa apakah string bilangan bulat positif
Anda dapat menggunakan
str.isdigit()
untuk memeriksa apakah string yang diberikan adalah bilangan bulat positif .Hasil sampel:
Periksa string sebagai positif / negatif - integer / float
str.isdigit()
kembaliFalse
jika string adalah angka negatif atau angka float. Sebagai contoh:Jika Anda ingin juga memeriksa bilangan bulat negatif dan
float
, maka Anda dapat menulis fungsi khusus untuk memeriksanya sebagai:Contoh Run:
Buang string "NaN" (bukan angka) sambil memeriksa nomor
Fungsi-fungsi di atas akan kembali
True
untuk string "NAN" (Bukan angka) karena untuk Python valid float yang mewakili itu bukan angka. Sebagai contoh:Untuk memeriksa apakah nomornya "NaN", Anda dapat menggunakan
math.isnan()
sebagai:Atau jika Anda tidak ingin mengimpor perpustakaan tambahan untuk memeriksanya, maka Anda cukup memeriksanya dengan membandingkannya dengan menggunakan sendiri
==
. Python kembaliFalse
ketikanan
float dibandingkan dengan dirinya sendiri. Sebagai contoh:Oleh karena itu, di atas fungsi
is_number
dapat diperbarui kembaliFalse
untuk"NaN"
sebagai:Contoh Run:
PS: Setiap operasi untuk setiap cek tergantung pada jenis nomor dilengkapi dengan overhead tambahan. Pilih versi
is_number
fungsi yang sesuai dengan kebutuhan Anda.sumber
Casting untuk mengapung dan menangkap ValueError mungkin adalah cara tercepat, karena float () secara khusus dimaksudkan untuk hal itu. Hal lain yang membutuhkan penguraian string (regex, dll) kemungkinan akan lebih lambat karena fakta bahwa itu tidak disetel untuk operasi ini. $ 0,02 saya
sumber
Anda dapat menggunakan string Unicode, mereka memiliki metode untuk melakukan apa yang Anda inginkan:
Atau:
http://www.tutorialspoint.com/python/string_isnumeric.htm
http://docs.python.org/2/howto/unicode.html
sumber
s.isdecimal()
memeriksa apakahs
string bilangan bulat non-negatif.s.isnumeric()
termasuk karakter yangint()
menolak.Saya ingin melihat metode mana yang tercepat. Secara keseluruhan hasil terbaik dan paling konsisten diberikan oleh
check_replace
fungsi. Hasil tercepat diberikan olehcheck_exception
fungsi, tetapi hanya jika tidak ada pengecualian yang dipecat - yang berarti kodenya adalah yang paling efisien, tetapi biaya tambahan untuk melempar pengecualian cukup besar.Harap perhatikan bahwa memeriksa cast yang sukses adalah satu-satunya metode yang akurat, misalnya, ini berfungsi dengan
check_exception
tetapi dua fungsi tes lainnya akan mengembalikan False untuk float yang valid:Berikut ini adalah kode benchmark:
Berikut adalah hasil dengan Python 2.7.10 pada 2017 MacBook Pro 13:
Berikut adalah hasil dengan Python 3.6.5 pada 2017 MacBook Pro 13:
Berikut adalah hasil dengan PyPy 2.7.13 pada 2017 MacBook Pro 13:
sumber
Jadi untuk menyatukan semuanya, memeriksa Nan, angka tak terbatas dan bilangan kompleks (tampaknya mereka ditentukan dengan j, bukan i, yaitu 1 + 2j) menghasilkan:
sumber
Inputnya mungkin sebagai berikut:
a="50"
b=50
c=50.1
d="50.1"
Input 1-Umum:
Input dari fungsi ini bisa menjadi segalanya!
Menemukan apakah variabel yang diberikan adalah numerik. String numerik terdiri dari tanda opsional, sejumlah digit, bagian desimal opsional, dan bagian eksponensial opsional. Jadi + 0123.45e6 adalah nilai numerik yang valid. Notasi heksadesimal (mis. 0xf4c3b00c) dan biner (mis. 0b10100111001) tidak diizinkan.
fungsi is_numeric
uji:
is_floatfungsi
Menemukan apakah variabel yang diberikan adalah float. string float terdiri dari tanda opsional, sejumlah digit, ...
uji:
apa itu ast ?
2- Jika Anda yakin bahwa konten variabel adalah String :
gunakan metode str.isdigit ()
Input 3-Angka:
mendeteksi nilai int:
mendeteksi float:
sumber
ast
"?Saya melakukan tes kecepatan. Mari kita katakan bahwa jika string cenderung menjadi bilangan, cobalah / kecuali strategi adalah yang tercepat yang mungkin. Jika string tidak mungkin menjadi bilangan dan Anda tertarik pada bilangan bulat Integer , layak untuk melakukan beberapa tes (isdigit plus heading '-'). Jika Anda tertarik untuk memeriksa nomor float, Anda harus menggunakan coba / kecuali kode whitout escape.
sumber
Saya perlu menentukan apakah string dilemparkan ke tipe dasar (float, int, str, bool). Setelah tidak menemukan apa pun di internet saya membuat ini:
Contoh
Anda dapat menangkap tipe dan menggunakannya
sumber
RyanN menyarankan
Tetapi ini tidak cukup berhasil, karena untuk pelampung yang cukup besar,
x-1 == x
mengembalikan true. Sebagai contoh,2.0**54 - 1 == 2.0**54
sumber
Saya pikir solusi Anda baik-baik saja, tapi ada adalah implementasi regexp yang benar.
Tampaknya ada banyak kebencian regexp terhadap jawaban-jawaban ini yang saya pikir tidak bisa dibenarkan, regexps bisa cukup bersih dan benar dan cepat. Itu benar-benar tergantung pada apa yang Anda coba lakukan. Pertanyaan aslinya adalah bagaimana Anda bisa "memeriksa apakah string dapat direpresentasikan sebagai angka (float)" (sesuai judul Anda). Agaknya Anda ingin menggunakan nilai numerik / float setelah Anda memeriksa apakah itu valid, dalam hal ini coba / kecuali Anda masuk akal. Tetapi jika, karena alasan tertentu, Anda hanya ingin memvalidasi bahwa string adalah a angkakemudian regex juga berfungsi dengan baik, tetapi sulit untuk mendapatkan yang benar. Saya pikir sebagian besar jawaban regex sejauh ini, misalnya, tidak mengurai string dengan benar tanpa bagian integer (seperti ".7") yang merupakan float sejauh menyangkut python. Dan itu agak sulit untuk diperiksa dalam satu regex di mana bagian fraksional tidak diperlukan. Saya sudah menyertakan dua regex untuk menunjukkan ini.
Itu memang memunculkan pertanyaan menarik tentang apa "angka" itu. Apakah Anda memasukkan "inf" yang valid sebagai float in python? Atau apakah Anda memasukkan angka yang merupakan "angka" tetapi mungkin tidak dapat direpresentasikan dengan python (seperti angka yang lebih besar dari float max).
Ada juga ambiguitas dalam cara Anda menguraikan angka. Misalnya, bagaimana dengan "--20"? Apakah ini "angka"? Apakah ini cara hukum untuk mewakili "20"? Python akan membiarkan Anda melakukan "var = --20" dan mengaturnya menjadi 20 (meskipun sebenarnya ini karena ia memperlakukannya sebagai ekspresi), tetapi float ("- 20") tidak berfungsi.
Bagaimanapun, tanpa info lebih lanjut, inilah regex yang saya percayai mencakup semua int dan mengapung seperti python mem-parsing mereka .
Beberapa contoh nilai pengujian:
Menjalankan kode pembandingan dalam jawaban @ ron-reiter menunjukkan bahwa regex ini sebenarnya lebih cepat dari regex normal dan jauh lebih cepat dalam menangani nilai-nilai buruk daripada pengecualian, yang masuk akal. Hasil:
sumber
sumber
1e6
untuk mewakili angka?Inilah cara sederhana saya untuk melakukannya. Katakanlah saya mengulang-ulang beberapa string dan saya ingin menambahkannya ke sebuah array jika mereka berubah menjadi angka.
Ganti myvar.apppend dengan operasi apa pun yang ingin Anda lakukan dengan string jika ternyata berupa angka. Idenya adalah untuk mencoba menggunakan operasi float () dan menggunakan kesalahan yang dikembalikan untuk menentukan apakah string adalah angka atau tidak.
sumber
Saya juga menggunakan fungsi yang Anda sebutkan, tetapi segera saya perhatikan bahwa string sebagai "Nan", "Inf" dan variasi itu dianggap sebagai angka. Jadi saya usulkan versi perbaikan fungsi Anda, yang akan mengembalikan false pada jenis input tersebut dan tidak akan gagal varian "1e3":
sumber
Kode ini menangani eksponen, mengapung, dan bilangan bulat, tanpa menggunakan regex.
sumber
Fungsi pembantu pengguna:
kemudian
sumber
Anda dapat menggeneralisasi teknik pengecualian dengan cara yang bermanfaat dengan mengembalikan nilai yang lebih berguna daripada Benar dan Salah. Misalnya fungsi ini menempatkan tanda kutip putaran tetapi meninggalkan angka sendiri. Itulah yang saya butuhkan untuk filter cepat dan kotor untuk membuat beberapa definisi variabel untuk R.
sumber
Saya sedang mengerjakan masalah yang mengarahkan saya ke utas ini, yaitu bagaimana mengubah kumpulan data menjadi string dan angka dengan cara yang paling intuitif. Saya menyadari setelah membaca kode asli bahwa apa yang saya butuhkan berbeda dalam dua cara:
1 - Saya ingin hasil integer jika string mewakili integer
2 - Saya ingin hasil angka atau string tetap pada struktur data
jadi saya mengadaptasi kode asli untuk menghasilkan turunan ini:
sumber
Coba ini.
sumber
is_number('10')
sumber