Bir dizenin sayı (float) olup olmadığını nasıl kontrol ederim?


1607

Bir dizenin Python'da sayı olarak gösterilip gösterilemeyeceğini kontrol etmenin en iyi yolu nedir?

Şu anda sahip olduğum fonksiyon:

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

Hangi, sadece çirkin ve yavaş değil, hantal görünüyor. Ancak daha iyi bir yöntem bulamadım çünkü floatana işlevi çağırmak daha da kötü.


61
Mevcut çözümünüzün nesi var? Kısa, hızlı ve okunabilir.
Albay Panik

5
Ve sadece Doğru ya da Yanlış dönmek zorunda değilsiniz. Bunun yerine uygun şekilde değiştirilmiş olan değeri döndürebilirsiniz - örneğin, sayı olmayanları tırnak içine almak için bunu kullanabilirsiniz.
Thruston

7
Başarılı bir dönüşüm durumunda float (lar) ın sonucunu döndürmek daha iyi olmaz mı? Hala başarı kontrolüne sahipsiniz (sonuç Yanlış) ve aslında yine de isteyebileceğiniz dönüşüm var.
Jiminion

8
Bu soru daha eski olmasına rağmen, bunun EAFP olarak belgelenmiş zarif bir yol olduğunu söylemek istedim . Yani muhtemelen bu tür bir problem için en iyi çözüm.
thiruvenkadam

7
Yapmayın başarısız üzerinde şamandıra (ler) veya Hiçbiri sonucunu döndürür. daha sonra x = float('0.00'); if x: use_float(x);kodunuzda bir hata var gibi kullanırsanız . Gerçek değerler, bu işlevlerin Noneilk etapta geri dönmektense istisna oluşturmasının nedenidir . Daha iyi bir çözüm, sadece yardımcı program işlevinden kaçınmak ve try catchkullanmak istediğinizde yüzer çağrıyı çevrelemektir .
Ovangle

Yanıtlar:


698

Hangi, sadece çirkin ve yavaş değil

Her ikisine de itiraz ederim.

Normal ifade veya başka bir dize ayrıştırma yöntemi daha çirkin ve yavaş olacaktır.

Yukarıdakilerden daha hızlı bir şey olabileceğinden emin değilim. İşlevi çağırır ve geri döner. Try / Catch çok fazla yük getirmez, çünkü en yaygın istisna yığın çerçevelerinin kapsamlı bir araması olmadan yakalanır.

Sorun, herhangi bir sayısal dönüştürme işlevinin iki tür sonucu olmasıdır

  • Sayı geçerliyse bir sayı
  • Geçerli bir numaranın ayrıştırılamayacağını gösteren bir durum kodu (örn. Errno aracılığıyla) veya istisna.

C (örnek olarak), bunun etrafında bir dizi yolu hacklemektedir. Python bunu açıkça ve açıkça ortaya koyuyor.

Bunu yapmak için kod mükemmel olduğunu düşünüyorum.


21
Ben kod mükemmel olduğunu sanmıyorum (ama çok yakın olduğunu düşünüyorum): sadece " tryyan tümcesinde " test "ediliyor parçası koymak daha olağan , bu yüzden return Truebir elsecümle koymak istiyorum try. Sebeplerden biri, sorudaki kodla, eğer onu gözden trygeçirmem gerekirse, cümledeki ikinci ifadenin bir ValueError oluşturamadığını kontrol etmem gerekecekti: verilen, bu çok fazla zaman veya beyin gücü gerektirmiyor, ama neden hiçbirine ihtiyaç duyulmadığında kullanılsın?
Eric O Lebigot

4
Cevabı çekici görünüyor, ancak neden kutudan çıkarılmadığını merak ediyor ... Bunu kopyalayacağım ve her durumda kullanacağım.
adaçayı

9
Nasıl korkunç. Nasıl sayı umurumda değil eğer ilgili olduğunu bir sayı (Beni buraya getiren budur) olduğunu sadece? 1 satır yerine IsNumeric()ya bir try / catch ya da başka bir try / catch sarma ile sonuçlanır. Ugh
Temel

6
'Kutudan çıktığı gibi' sağlanmamıştır, çünkü if is_number(s): x = float(x) else: // failile aynı kod satırı sayısıdır try: x = float(x) catch TypeError: # fail. Bu yardımcı program işlevi tamamen gereksiz bir soyutlamadır.
ovangle

12
Ancak soyutlama, kütüphanelerin bütün noktasıdır. 'İsNumber' fonksiyonuna (herhangi bir dilde) sahip olmak büyük miktarda yardımcı olur, çünkü bunu doğrudan if ifadeleri haline getirebilir ve try-catch bloklarına dayanan çok daha okunabilir ve bakımı kolay bir koda sahip olabilirsiniz. Ayrıca, kodu birden fazla sınıfta / modülde birden çok kez kullanmanız gerekiyorsa, yerleşik bir işlevin sahip olacağından daha fazla kod satırı kullandınız.
JamEngulfer

1612

Kayan noktalar yerine (pozitif, işaretsiz) tamsayıları ayrıştırmak istiyorsanız, isdigit()işlevi dize nesneleri için kullanabilirsiniz .

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

Dize Yöntemleri - isdigit(): Python2 , Python3

Unicode dizelerinde de Unicode'a aşina olmadığım bir şey var - Ondalık / ondalık


232
Negatifler için de olumsuz
intrepion

22
Çok exponentials başarısız: '1e3'.isdigit () -> Yanlış
ssc

35
Number! = Digit olsa da, bir dizenin bir tamsayı içerip içermediğini test etmenin yollarını arayan insanlar bu soru boyunca çok iyi yanabilir ve isDigit yaklaşımı uygulamaları için mükemmel bir şekilde uygun olabilir.
Adam Parkin

8
: @AdamParkin isdigit()ve int()Unicode karakter için, bir tamsayı örneğin ne olduğu konusunda farklı görüşler vardır u'\u00b9': u'¹'.isdigit()olup Trueancak int(u'¹')ValueError yükseltir.
jfs

6
+1: isdigit (), OP'nin aradığı şey olmayabilir, ama tam olarak istediğim şeydi. Bu cevap ve yöntemin tüm sayı türlerini kapsamaması söz konusu olmayabilir, ancak doğruluğuna ilişkin argümanların aksine, hala yüksek derecede önemlidir. "Sayı! = Rakam" rakamı hala sayının bir alt kümesidir, özellikle pozitif, negatif olmayan ve 1-10 tabanını kullanan sayılar. Ayrıca, bu yöntem, bir dizenin sayısal bir kimlik olup olmadığını kontrol etmek istediğiniz durumlar için özellikle yararlı ve kısadır;
Justin Johnson

161

TL; DR En iyi çözüms.replace('.','',1).isdigit()

Farklı yaklaşımları karşılaştırarak bazı kriterler yaptım

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

Dize bir sayı değilse, outside-block oldukça yavaştır. Ancak daha da önemlisi, deneme-dışı yöntemi, bilimsel gösterimleri doğru şekilde işleyen tek yaklaşımdır.

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


Kayan nokta " .1234 " şu kişiler tarafından desteklenmez: - 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__)

Bilimsel gösterim "1.000000e + 50" şu kişiler tarafından desteklenmez:
- is_number_regex
- is_number_repl_isdigit
Bilimsel gösterim "1e50" şu kişiler tarafından desteklenmez:
- is_number_regex
- is_number_repl_isdigit

EDIT: Karşılaştırma sonuçları

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

Aşağıdaki fonksiyonların test edildiği yer

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

resim açıklamasını buraya girin


15
güzel grafikler için +1. Ben kıyaslama ve grafik gördüm, tüm TL; DR şey net ve sezgisel oldu.
jcchuks

@JCChuks ile hemfikirim: grafik tüm TL'yi elde etmek için çok yardımcı olur; DR hızlı bir şekilde. Ama bence bir TL; DR ( TL: DR : en iyi çözüm s.replace('.','',1).isdigit()) bu anwser'ın başında görünmelidir. Her durumda kabul edilen olmalıdır. Teşekkürler!
Simon

10
Bu yöntem negatif sayıları (tire) işlemez. Hatalara daha az eğilimli olduğu ve her seferinde çalışacağı için şamandıra yöntemini kullanmayı savunacağım.
Urchin

3
Unutulmaması gereken şey, varsayımda bile, tire işareti bulunmadığında, replace-isdigit yönteminin yalnızca sayı olmayanlar için (Yanlış sonuç) daha hızlı olmasına karşın, deneme-hariç tutma yöntemi sayılar için daha hızlıdır (Gerçek sonuç). Girişinizin çoğu geçerli girişse, try-hariç çözümüyle daha iyi durumdasınız!
Markus von Broady

1
'1.5e-9'Negatif gibi veya negatif olarak üstel gösterim üzerinde çalışmaz .
EL_DON

68

Dikkate almak isteyebileceğiniz bir istisna vardır: 'NaN' dizesi

İs_number'ın 'NaN' için FALSE döndürmesini istiyorsanız, Python bunu sayı olmayan bir sayıyı temsil etmeye dönüştürdüğü için bu kod çalışmaz (kimlik sorunları hakkında konuşun):

>>> float('NaN')
nan

Aksi takdirde, aslında şimdi yaygın olarak kullandığım kod parçası için teşekkür etmeliyim. :)

G.


2
Aslında, iletilen metin aslında bir sayının temsili değilse, NaNdönmek (yerine False) için iyi bir değer olabilir . Bunu kontrol etmek bir tür acıdır (Python'un floattipi gerçekten bunun için bir yönteme ihtiyaç duyar), ancak hata üretmeden hesaplamalarda kullanabilirsiniz ve sadece sonucu kontrol etmeniz gerekir.
tür

7
Başka bir istisna dizedir 'inf'. Ya infya NaNda öneki bir +veya ile önek -olarak kabul edilebilir ve yine de kabul edilebilir.
agf

4
NaN ve Inf için False değerini döndürmek istiyorsanız, satırı x = float (s) olarak değiştirin; dönüş (x == x) ve (x - 1! = x). Bu, Inf ve NaN hariç tüm şamandıralar için True döndürmelidir
RyanN

5
x-1 == xküçük şamandıralar için geçerlidir inf. Python 3.2'den math.isfinite, ne NaN ne de sonsuz olan sayıları test etmek veya her ikisini math.isnanve math.isinföncesinde kontrol etmek için kullanabilirsiniz.
Steve Jessop

56

buna ne dersin:

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

ki bu sadece bir tane ya da hiç '.' rakamlar dizesinde.

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

yanlış dönecek

edit: sadece başka bir yorum gördüm ... .replace(badstuff,'',maxnum_badstuff)başka durumlar için bir ekleme yapılabilir. Eğer tuzdan geçiyorsanız ve keyfi çeşniler (ref: xkcd # 974 ) geçiriyorsanız , bu iyi olacaktır:


7
Ancak bu negatif sayıları açıklamaz.
Michael Barton

5
Veya gibi üslü sayılar 1.234e56(aynı zamanda +1.234E+56ve daha fazla varyant olarak da yazılabilir ).
Alfe

re.match(r'^[+-]*(0[xbo])?[0-9A-Fa-f]*\.?[0-9A-Fa-f]*(E[+-]*[0-9A-Fa-f]+)$', 'str')bir sayı belirlemede daha iyi bir iş yapmalı (ama hepsi değil, bunu iddia etmiyorum). Sorucunun orijinal kodunu kullanmak için çok daha iyi tavsiye etmiyorum.
Baldrickk

Bu çözümden beğenmezseniz okumak bu downvoting önce!
aloisdg moving codidact.com için

adam bu, bu web sitesinde gördüğüm en akıllı çözüm !, güzel yapılmış adam!
Karam Qusai

41

Hangi, sadece çirkin ve yavaş değil, hantal görünüyor.

Alışmak biraz zaman alabilir, ancak bunu yapmanın pitonik yolu budur. Daha önce de belirtildiği gibi, alternatifler daha kötüdür. Ancak işleri bu şekilde yapmanın başka bir avantajı daha var: polimorfizm.

Ördek yazmanın arkasındaki ana fikir, "eğer bir ördek gibi yürür ve konuşursa, o zaman bir ördek" dir. Bir şeyin kayan noktaya dönüştürülüp dönüştürülemeyeceğini nasıl belirleyeceğinizi değiştirebilmeniz için dizeyi alt sınıflamaya ihtiyacınız olduğuna karar verirseniz ne olur? Ya da başka bir nesneyi tamamen test etmeye karar verirseniz ne olur? Yukarıdaki kodu değiştirmek zorunda kalmadan bunları yapabilirsiniz.

Diğer diller bu sorunları arayüzler kullanarak çözmektedir. Başka bir iş parçacığı için hangi çözümün daha iyi olduğunu analiz edeceğim. Bununla birlikte, nokta, python'un kesinlikle denklemin ördek yazma tarafında olduğu ve muhtemelen Python'da çok fazla programlama yapmayı planlıyorsanız, bu gibi bir sözdizimine alışmanız gerekecek (ancak bu, tabii ki beğenmek zorunda).

Dikkate almak isteyebileceğiniz başka bir şey: Python, diğer birçok dile kıyasla (örneğin .Net'ten 30 kat daha hızlı) istisnaları atma ve yakalama konusunda oldukça hızlıdır. Heck, dilin kendisi, istisnai olmayan, normal program koşullarını (for döngüsü kullandığınızda) iletmek için istisnalar bile atar. Bu nedenle, önemli bir sorun fark edene kadar bu kodun performans yönleri hakkında çok fazla endişelenmeyeceğim.


1
Python'un temel işlevler için istisnalar kullandığı bir diğer yaygın yer hasattr(), sadece getattr()a try/except. Yine de, istisna işleme normal akış kontrolünden daha yavaştır, bu yüzden bunu çoğu zaman doğru olacak bir şey için kullanmak performans cezasına neden olabilir.
tür

Görünüşe göre, bir astar istiyorsanız, SOL
Basic

Ayrıca, pitonik, ucuz istisnalara sahip olmanın etkisiyle ilgili olarak "izin vermekten daha affedilmeyi istemenin daha iyi olduğu" fikridir.
heltonbiker

40

Alfe'nin işaretlemesinden sonra güncellenen, karmaşık tutamakların her ikisini de ayrı olarak kontrol etmenize gerek yoktur:

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

    return True

Daha önce söylenenler: Bir kayan nokta ile temsil edilemeyen karmaşık sayıları (örn. 1 + 2i) kontrol etmeniz gerekebilir.

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

14
Katılmıyorum. Bu normal kullanımda ÇOK olası değildir ve% 0.0001'lik bir yanlış çalışma şansı için ekstra işlemle bir çağrı yapmak yerine, bunları kullanırken bir is_complex_number () çağrısı oluşturmak daha iyi olur.
Jiminion

3
Sen şerit olabilir float()tamamen malzeme ve sadece kontrol complex()çağrı başarılı. Tarafından ayrıştırılan her şey tarafından float()ayrıştırılabilir complex().
Alfe

Bu işlev, Pandaların NaN'lerini ve Inf değerlerini sayısal değerler olarak döndürür.
fixxxer

complex('(01989)')dönecektir (1989+0j). Ama float('(01989)')başarısız olacak. Bence kullanmak complexiyi bir fikir değil.
plhn

26

Bunu intkullanmak için:

>>> "1221323".isdigit()
True

Ama floatbazı hilelere ihtiyacımız var ;-). Her kayan nokta numarasının bir noktası vardır ...

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

Ayrıca negatif sayılar için lstrip()şunları ekleyin :

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

Ve şimdi evrensel bir yol elde ediyoruz:

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

2
Gibi 1.234e56ve benzeri şeyleri ele almaz . Ayrıca, 99999999999999999999e99999999999999999999bunun bir sayı olmadığını nasıl bulacağınızı merak ediyorum . Ayrıştırmaya çalışmak çabucak öğrenir.
Alfe

Bu işlem 50 metrelik diziler listesinde kabul edilen çözümden ~% 30 daha hızlı ve 5k dizeler listesinde% 150 daha hızlı çalışır. 👏
Zev Averbach

15

Sadece Mimik C #

C # 'da skaler değerlerin ayrıştırılmasını sağlayan iki farklı işlev vardır:

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

) (Float.parse:

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

Not: Neden istisnayı bir TypeError olarak değiştirdiğimi merak ediyorsanız, işte belgeler .

) (Float.try_parse:

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

Not: Boolean 'False' değerini döndürmek istemezsiniz, çünkü bu hala bir değer türüdür. Hiçbiri daha iyi değildir çünkü başarısızlığı gösterir. Tabii ki, farklı bir şey istiyorsanız fail parametresini istediğiniz gibi değiştirebilirsiniz.

Şamandırayı 'parse ()' ve 'try_parse ()' içerecek şekilde genişletmek için, bu yöntemleri eklemek üzere 'float' sınıfını monkeypatch yapmanız gerekir.

Önceden var olan işlevlere saygı duymak istiyorsanız, kod şöyle olmalıdır:

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

SideNote: Ben şahsen Monkey Punching demeyi tercih ediyorum çünkü bunu yaptığımda dili kötüye kullandığımı hissediyorum ama YMMV.

Kullanımı:

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

Ve büyük Sage Pythonas, Holy See Sharpisus'a, "Yapabileceğin her şeyi daha iyi yapabilirim; senden daha iyi bir şey yapabilirim" dedi.


Son zamanlarda çoğunlukla JS kodlama ve aslında bazı küçük hatalar olabilir bu yüzden bu test etmedi. Herhangi birini görürseniz, hatalarımı düzeltmekten çekinmeyin.
Evan Plaice

Karmaşık numaralara destek eklemek için @Matthew Wilcoxson tarafından verilen cevaba bakınız. stackoverflow.com/a/3335060/290340 .
Evan Plaice

1
!Bunun yerine kullanmak notküçük bir hata olabilir, ancak kesinlikle yerleşik floatCPython'a nitelik atayamazsınız .
BlackJack

15

Sayı olmayan dizeler için, try: except:normal ifadelerden daha yavaştır. Geçerli sayı dizeleri için normal ifade daha yavaştır. Bu nedenle, uygun yöntem girdinize bağlıdır.

Bir performans bağlama içinde olduğunuzu fark ederseniz, isploat adlı bir işlev sağlayan fastnumbers adlı yeni bir üçüncü taraf modülü kullanabilirsiniz . Tam açıklama, ben yazarım. Sonuçları aşağıdaki zamanlamalara dahil ettim.


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

Gördüğün gibi

  • try: except: sayısal girdi için hızlı, geçersiz girdi için çok yavaş
  • regex giriş geçersiz olduğunda çok etkilidir
  • fastnumbers her iki durumda da kazanır

Düzeltilmiş duruyorum: -} sadece bunu yapıyormuş gibi görünmüyordu. Belki isimler gibi kullanarak prep_code_basisve prep_code_re_methodhatamı engel olurdu.
Alfe

En azından isfloatişlev için modülünüzün nasıl çalıştığını açıklar mısınız?
Solomon Ucko

@SolomonUcko Dize kontrol kısmı için kaynak koduna bir link: github.com/SethMMorton/fastnumbers/blob/v1.0.0/src/… . Temel olarak, dizideki her karakterde sırayla yürür ve geçerli bir kayan nokta için bir kalıp izlediğini doğrular. Giriş zaten bir sayı ise, sadece hızlı PyFloat_Check kullanır .
SethMMorton

1
Bu konuya en iyi alternatifleri karşı test ben bu çözüm teyit farkla en hızlı. İkinci en hızlı yöntem str(s).strip('-').replace('.','',1).isdigit()yaklaşık 10 kat daha yavaştır!
Alexander McFarlane

14

Bu özellikle eski olduğunu biliyorum ama ben bulan herkes için çok değerli olabilir en yüksek oyu cevap eksik bilgileri kapsar inanıyorum bir cevap eklemek istiyorum:

Aşağıdaki yöntemlerin her biri için, kabul edilecek herhangi bir girdiye ihtiyacınız varsa bunları bir sayıyla bağlayın. (0-255 yerine tamsayıların vokal tanımlarını kullandığımızı varsayarsak)

x.isdigit() x'in bir tam sayı olup olmadığını kontrol etmek için iyi çalışır.

x.replace('-','').isdigit() x'in negatif olup olmadığını kontrol etmek için iyi çalışır. (Check-in first position)

x.replace('.','').isdigit() x'in ondalık olup olmadığını kontrol etmek için iyi çalışır.

x.replace(':','').isdigit() x'in oran olup olmadığını kontrol etmek için iyi çalışır.

x.replace('/','',1).isdigit() x'in bir kesir olup olmadığını kontrol etmek için iyi çalışır.


1
Kesirler için olsa da, muhtemelen yapmanız gerekir x.replace('/','',1).isdigit()ya da 4/7/2017 gibi tarihler sayı olarak yanlış yorumlanır.
Yuxuan Chen

Koşulları zincirlemenin en iyi yolları için: stackoverflow.com/q/3411771/5922329
Daniel Braun

13

Bu cevap dizeyi bulmak için örneklerle fonksiyona sahip adım adım kılavuz sağlar:

  • Pozitif tamsayı
  • Pozitif / negatif - tamsayı / kayan sayı
  • Numarayı denetlerken "NaN" (sayı değil) dizeleri nasıl silinir?

Dizenin pozitif tamsayı olup olmadığını kontrol edin

str.isdigit()Verilen dizenin pozitif tamsayı olup olmadığını kontrol etmek için kullanabilirsiniz .

Örnek Sonuçlar:

# For digit
>>> '1'.isdigit()
True
>>> '1'.isalpha()
False

Dizeyi pozitif / negatif - tamsayı / kayan nokta olarak denetleme

str.isdigit()Falsedize negatif bir sayı veya kayan sayı ise döndürür . Örneğin:

# returns `False` for float
>>> '123.3'.isdigit()
False
# returns `False` for negative number
>>> '-123'.isdigit()
False

Negatif tamsayıları da kontrol etmekfloat istiyorsanız ve o zaman kontrol etmek için özel bir işlev yazabilirsiniz:

def is_number(n):
    try:
        float(n)   # Type-casting the string to `float`.
                   # If string is not a valid `float`, 
                   # it'll raise `ValueError` exception
    except ValueError:
        return False
    return True

Örnek Çalışma:

>>> is_number('123')    # positive integer number
True

>>> is_number('123.4')  # positive float number
True

>>> is_number('-123')   # negative integer number
True

>>> is_number('-123.4') # negative `float` number
True

>>> is_number('abc')    # `False` for "some random" string
False

Numarayı denetlerken "NaN" (sayı değil) dizelerini atın

Yukarıdaki işlevler True"NAN" (sayı değil) dizesi için dönecektir , çünkü Python için bir sayı olmadığı için geçerli kayan noktalı sayıdır. Örneğin:

>>> is_number('NaN')
True

Numaranın "NaN" olup olmadığını kontrol etmek için şu şekilde kullanabilirsiniz math.isnan():

>>> import math
>>> nan_num = float('nan')

>>> math.isnan(nan_num)
True

Veya bunu kontrol etmek için ek kitaplığı içe aktarmak istemiyorsanız, kullanarak kitaplığı kendisiyle karşılaştırarak kontrol edebilirsiniz ==. Python döndürür Falsezaman nanşamandıra kendisi ile karşılaştırılır. Örneğin:

# `nan_num` variable is taken from above example
>>> nan_num == nan_num
False

Bu nedenle, yukarıdaki işlev is_numberşu şekilde dönmek Falseiçin güncellenebilir"NaN" :

def is_number(n):
    is_number = True
    try:
        num = float(n)
        # check for "nan" floats
        is_number = num == num   # or use `math.isnan(num)`
    except ValueError:
        is_number = False
    return is_number

Örnek Çalışma:

>>> is_number('Nan')   # not a number "Nan" string
False

>>> is_number('nan')   # not a number string "nan" with all lower cased
False

>>> is_number('123')   # positive integer
True

>>> is_number('-123')  # negative integer
True

>>> is_number('-1.12') # negative `float`
True

>>> is_number('abc')   # "some random" string
False

Not: Her kontrol için numara türüne bağlı olarak yapılan her işlem ek yük ile birlikte gelir. is_numberİhtiyacınıza uygun işlevin sürümünü seçin .


12

Kayan noktaya yayın yapmak ve ValueError'u yakalamak muhtemelen en hızlı yoldur, çünkü float () özellikle bunun içindir. Dize ayrıştırma (regex vb.) Gerektiren herhangi bir şey, bu işlem için ayarlanmadığı için muhtemelen daha yavaş olacaktır. Benim 0.02 $.


11
"2e-2" dolarlarınız da bir
floattır

8
@tzot ASLA parasal bir değeri temsil etmek için kayan nokta kullanmayın.
Luke

6
@ Luke: Size kesinlikle katılıyorum, ancak parasal değerleri temsil etmek için şamandıra kullanmayı asla önermedim; Sadece bu parasal değerler söz konusu olabilir :) baskını gibi temsil edilebilir
tzot

11

Unicode dizelerini kullanabilirsiniz, tam olarak istediğinizi yapmak için bir yöntemi vardır:

>>> s = u"345"
>>> s.isnumeric()
True

Veya:

>>> s = "345"
>>> u = unicode(s)
>>> u.isnumeric()
True

http://www.tutorialspoint.com/python/string_isnumeric.htm

http://docs.python.org/2/howto/unicode.html


2
negatif olmayan ints için sorun yok ;-)
andilabs 22:14

1
s.isdecimal()sdizenin negatif olmayan bir tam sayı olup olmadığını kontrol eder . reddedilen s.isnumeric()karakterler içeriyor int().
jfs

9

Hangi yöntemin en hızlı olduğunu görmek istedim. Genel olarak check_replacefonksiyon tarafından en iyi ve en tutarlı sonuçlar verilmiştir . En hızlı sonuçlar check_exceptionfonksiyon tarafından verildi , ancak sadece istisna yoksa - kodunun en verimli olduğu anlamına gelir, ancak bir istisna atma yükü oldukça büyüktür.

Başarılı bir dökümün kontrol edilmesinin doğru olan tek yöntem olduğunu lütfen unutmayın, örneğin, bununla çalışır, check_exceptionancak diğer iki test işlevi geçerli bir şamandıra için False değerini döndürür:

huge_number = float('1e+100')

Kıyaslama kodu:

import time, re, random, string

ITERATIONS = 10000000

class Timer:    
    def __enter__(self):
        self.start = time.clock()
        return self
    def __exit__(self, *args):
        self.end = time.clock()
        self.interval = self.end - self.start

def check_regexp(x):
    return re.compile("^\d*\.?\d*$").match(x) is not None

def check_replace(x):
    return x.replace('.','',1).isdigit()

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

to_check = [check_regexp, check_replace, check_exception]

print('preparing data...')
good_numbers = [
    str(random.random() / random.random()) 
    for x in range(ITERATIONS)]

bad_numbers = ['.' + x for x in good_numbers]

strings = [
    ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(random.randint(1,10)))
    for x in range(ITERATIONS)]

print('running test...')
for func in to_check:
    with Timer() as t:
        for x in good_numbers:
            res = func(x)
    print('%s with good floats: %s' % (func.__name__, t.interval))
    with Timer() as t:
        for x in bad_numbers:
            res = func(x)
    print('%s with bad floats: %s' % (func.__name__, t.interval))
    with Timer() as t:
        for x in strings:
            res = func(x)
    print('%s with strings: %s' % (func.__name__, t.interval))

İşte 2017 MacBook Pro 13'te Python 2.7.10 ile sonuçlar:

check_regexp with good floats: 12.688639
check_regexp with bad floats: 11.624862
check_regexp with strings: 11.349414
check_replace with good floats: 4.419841
check_replace with bad floats: 4.294909
check_replace with strings: 4.086358
check_exception with good floats: 3.276668
check_exception with bad floats: 13.843092
check_exception with strings: 15.786169

İşte 2017 MacBook Pro 13'te Python 3.6.5 ile sonuçlar:

check_regexp with good floats: 13.472906000000009
check_regexp with bad floats: 12.977665000000016
check_regexp with strings: 12.417542999999995
check_replace with good floats: 6.011045999999993
check_replace with bad floats: 4.849356
check_replace with strings: 4.282754000000011
check_exception with good floats: 6.039081999999979
check_exception with bad floats: 9.322753000000006
check_exception with strings: 9.952595000000002

İşte 2017 MacBook Pro 13'te PyPy 2.7.13 ile sonuçlar:

check_regexp with good floats: 2.693217
check_regexp with bad floats: 2.744819
check_regexp with strings: 2.532414
check_replace with good floats: 0.604367
check_replace with bad floats: 0.538169
check_replace with strings: 0.598664
check_exception with good floats: 1.944103
check_exception with bad floats: 2.449182
check_exception with strings: 2.200056

10
Performansı geçersiz durumlar için de test etmelisiniz. Tam olarak "yavaş" kısım olan bu sayılarla ilgili bir istisna yoktur.
Ugo Méda

1
@ UgoMéda 2013 tavsiye aldım ve yaptım :)
Ron Reiter

"Başarılı bir oyuncu seçimi yapmanın doğru olan tek yöntem olduğunu lütfen unutmayın" <- bu aslında doğru değil. Testimi yukarıdaki cevabımda normal ifadeyi kullanarak çalıştırdım ve aslında normal ifadeden daha hızlı çalışıyor. Sonuçları yukarıdaki cevabıma ekleyeceğim.
David Ljung Madison Stellar

Bu arada, eğlenceli bir nokta olarak, kötü sayı yaratıcısı aslında oldukça nadir olsa da, bazı yasal sayılar oluşturabilir. :)
David Ljung Madison Stellar

8

Yani hepsini bir araya getirmek, Nan, sonsuzluk ve karmaşık sayıları kontrol etmek (i ile değil, j ile belirtilmiş gibi görünüyor, yani 1 + 2j) ile sonuçlanır:

def is_number(s):
    try:
        n=str(float(s))
        if n == "nan" or n=="inf" or n=="-inf" : return False
    except ValueError:
        try:
            complex(s) # for complex
        except ValueError:
            return False
    return True

Şimdiye kadar en iyi cevap. Teşekkürler
anish

6

Giriş aşağıdaki gibi olabilir:

a="50" b=50 c=50.1 d="50.1"


1-Genel giriş:

Bu fonksiyonun girişi her şey olabilir!

Verilen değişkenin sayısal olup olmadığını bulur. Sayısal dizeler isteğe bağlı işaret, herhangi bir sayı basamağı, isteğe bağlı ondalık bölüm ve isteğe bağlı üstel bölümden oluşur. Böylece + 0123.45e6 geçerli bir sayısal değerdir. Onaltılık (örn. 0xf4c3b00c) ve ikili (örn. 0b10100111001) gösterimine izin verilmez.

is_numeric işlevi

import ast
import numbers              
def is_numeric(obj):
    if isinstance(obj, numbers.Number):
        return True
    elif isinstance(obj, str):
        nodes = list(ast.walk(ast.parse(obj)))[1:]
        if not isinstance(nodes[0], ast.Expr):
            return False
        if not isinstance(nodes[-1], ast.Num):
            return False
        nodes = nodes[1:-1]
        for i in range(len(nodes)):
            #if used + or - in digit :
            if i % 2 == 0:
                if not isinstance(nodes[i], ast.UnaryOp):
                    return False
            else:
                if not isinstance(nodes[i], (ast.USub, ast.UAdd)):
                    return False
        return True
    else:
        return False

Ölçek:

>>> is_numeric("54")
True
>>> is_numeric("54.545")
True
>>> is_numeric("0x45")
True

is_float işlevi

Verilen değişkenin kayan noktalı olup olmadığını bulur. şamandıra dizeleri isteğe bağlı işareti, herhangi bir sayı basamak, oluşur ...

import ast

def is_float(obj):
    if isinstance(obj, float):
        return True
    if isinstance(obj, int):
        return False
    elif isinstance(obj, str):
        nodes = list(ast.walk(ast.parse(obj)))[1:]
        if not isinstance(nodes[0], ast.Expr):
            return False
        if not isinstance(nodes[-1], ast.Num):
            return False
        if not isinstance(nodes[-1].n, float):
            return False
        nodes = nodes[1:-1]
        for i in range(len(nodes)):
            if i % 2 == 0:
                if not isinstance(nodes[i], ast.UnaryOp):
                    return False
            else:
                if not isinstance(nodes[i], (ast.USub, ast.UAdd)):
                    return False
        return True
    else:
        return False

Ölçek:

>>> is_float("5.4")
True
>>> is_float("5")
False
>>> is_float(5)
False
>>> is_float("5")
False
>>> is_float("+5.4")
True

nedir ast ?


2- Değişken içeriğin String olduğundan eminseniz :

Kullanım str.isdigit () metodu

>>> a=454
>>> a.isdigit()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute 'isdigit'
>>> a="454"
>>> a.isdigit()
True

3-Sayısal giriş:

int değerini tespit et:

>>> isinstance("54", int)
False
>>> isinstance(54, int)
True
>>> 

şamandıra tespit:

>>> isinstance("45.1", float)
False
>>> isinstance(45.1, float)
True

" ast" nedir?

4

Biraz hız testi yaptım. Dize ise Diyelim ki muhtemel sayı olması haricinde / deneyin strateji dizedir hızlı possible.If olduğu olası değildir sayı olması ve ilgilendiğiniz Tamsayı çek, bazı testi (isdigit artı başlığı yapmak worths '-'). Şamandıra numarasını kontrol etmek istiyorsanız, kaçış denemesi / hariç kodu kullanmanız gerekir .


4

Bir dize temel türleri (float, int, str, bool) döküm belirlemek için gerekli. İnternette bir şey bulamadıktan sonra bunu yarattım:

def str_to_type (s):
    """ Get possible cast type for a string

    Parameters
    ----------
    s : string

    Returns
    -------
    float,int,str,bool : type
        Depending on what it can be cast to

    """    
    try:                
        f = float(s)        
        if "." not in s:
            return int
        return float
    except ValueError:
        value = s.upper()
        if value == "TRUE" or value == "FALSE":
            return bool
        return type(s)

Misal

str_to_type("true") # bool
str_to_type("6.0") # float
str_to_type("6") # int
str_to_type("6abc") # str
str_to_type(u"6abc") # unicode       

Türü yakalayabilir ve kullanabilirsiniz

s = "6.0"
type_ = str_to_type(s) # float
f = type_(s) 

3

RyanN öneriyor

NaN ve Inf için False değerini döndürmek istiyorsanız, satırı x = float (s) olarak değiştirin; dönüş (x == x) ve (x - 1! = x). Bu Inf ve NaN hariç tüm şamandıralar için True döndürmelidir

Ancak bu pek işe yaramaz, çünkü yeterince büyük şamandıralar için x-1 == xdoğrudur. Örneğin,2.0**54 - 1 == 2.0**54


3

Çözümünüzün iyi olduğunu düşünüyorum, ancak doğru bir normal ifade uygulaması var.

Gereksiz olduğunu düşündüğüm bu cevaplara çok fazla regexp nefreti var gibi görünüyor, regexps makul temiz ve doğru ve hızlı olabilir. Gerçekten ne yapmaya çalıştığınıza bağlı. Asıl soru, "bir dizenin sayı (float) olarak gösterilip gösterilemeyeceğini nasıl kontrol edebileceğiniz" idi (başlığınıza göre). Muhtemelen, geçerli olduğunu kontrol ettikten sonra sayısal / kayan değeri kullanmak istersiniz, bu durumda denemeniz / hariç tutmanız çok mantıklıdır. Ancak, bir nedenden ötürü, bir dizenin bir sayıbir normal ifade de iyi çalışır, ancak düzeltilmesi zordur. Regex cevapları çoğu şimdiye kadar, örneğin, python söz konusu olduğunda bir şamandıra olan bir tamsayı (".7" gibi) olmadan dizeleri ayrıştırmak sanmıyorum. Ve kesirli kısmın gerekli olmadığı tek bir regex'te kontrol etmek biraz zor. Bunu göstermek için iki normal ifade ekledim.

Bir "sayı" nın ne olduğu hakkında ilginç bir soru ortaya çıkarır. Python'da şamandıra olarak geçerli olan "inf" ekliyor musunuz? Veya "sayılar" olan ancak python ile temsil edilemeyen sayılar ekliyor musunuz (şamandıra maks. Büyük olan sayılar gibi).

Sayıları ayrıştırma konusunda da belirsizlikler var. Örneğin, "--20" ne olacak? Bu bir "sayı" mı? Bu, "20" yi temsil etmenin yasal bir yolu mu? Python "var = --20" yapmanıza ve onu 20'ye ayarlamanıza izin verecektir (aslında bunun bir ifade olarak davranmasıdır), ancak float ("- 20") çalışmaz.

Neyse, daha fazla bilgi olmadan, python onları ayrıştırırken tüm ints ve yüzen kapsar inanıyorum bir regex .

# Doesn't properly handle floats missing the integer part, such as ".7"
SIMPLE_FLOAT_REGEXP = re.compile(r'^[-+]?[0-9]+\.?[0-9]+([eE][-+]?[0-9]+)?$')
# Example "-12.34E+56"      # sign (-)
                            #     integer (12)
                            #           mantissa (34)
                            #                    exponent (E+56)

# Should handle all floats
FLOAT_REGEXP = re.compile(r'^[-+]?([0-9]+|[0-9]*\.[0-9]+)([eE][-+]?[0-9]+)?$')
# Example "-12.34E+56"      # sign (-)
                            #     integer (12)
                            #           OR
                            #             int/mantissa (12.34)
                            #                            exponent (E+56)

def is_float(str):
  return True if FLOAT_REGEXP.match(str) else False

Bazı test değerleri örnekleri:

True  <- +42
True  <- +42.42
False <- +42.42.22
True  <- +42.42e22
True  <- +42.42E-22
False <- +42.42e-22.8
True  <- .42
False <- 42nope

Kıyaslama kodunun @ ron-reiter'in cevabında çalıştırılması , bu normal ifadenin aslında normal normal ifadeden daha hızlı olduğunu ve kötü değerlerin ele alınmasında istisnadan çok daha hızlı olduğunu gösterir. Sonuçlar:

check_regexp with good floats: 18.001921
check_regexp with bad floats: 17.861423
check_regexp with strings: 17.558862
check_correct_regexp with good floats: 11.04428
check_correct_regexp with bad floats: 8.71211
check_correct_regexp with strings: 8.144161
check_replace with good floats: 6.020597
check_replace with bad floats: 5.343049
check_replace with strings: 5.091642
check_exception with good floats: 5.201605
check_exception with bad floats: 23.921864
check_exception with strings: 23.755481

Umarım bu doğru - herhangi bir karşı örnek hakkında duymak isteriz. :)
David Ljung Madison Stellar

2
import re
def is_number(num):
    pattern = re.compile(r'^[-+]?[-0-9]\d*\.\d*|[-+]?\.?[0-9]\d*$')
    result = pattern.match(num)
    if result:
        return True
    else:
        return False


​>>>: is_number('1')
True

>>>: is_number('111')
True

>>>: is_number('11.1')
True

>>>: is_number('-11.1')
True

>>>: is_number('inf')
False

>>>: is_number('-inf')
False

2
1e6Bir sayıyı temsil etmeyi düşünmüyor musunuz ?
Mark Dickinson

1

İşte bunu yapmanın basit yolu. Diyelim ki bazı dizeler arasında döngü yapıyorum ve sayı olduğu ortaya çıkarsa bunları bir diziye eklemek istiyorum.

try:
    myvar.append( float(string_to_check) )
except:
    continue

Bir sayı olduğu ortaya çıkarsa, myvar.apppend öğesini dize ile yapmak istediğiniz herhangi bir işlemle değiştirin. Fikir, bir float () işlemi kullanmak ve dizenin bir sayı olup olmadığını belirlemek için döndürülen hatayı kullanmaktır.


Dizide yanlış bir şey olması durumunda, istisnanın yanlışlıkla tetiklenmesini önlemek için bu işlevin ekleme kısmını başka bir ifadeye taşımalısınız.
DarwinSurvivor

1

Bahsettiğiniz işlevi de kullandım, ancak yakında dizelerin "Nan", "Inf" ve varyasyonunun sayı olarak kabul edildiğini fark ediyorum. Bu nedenle, bu tür girişlerde yanlış döndürecek ve "1e3" varyantlarında başarısız olmayacak şekilde, işlevinizin geliştirilmiş sürümünü öneriyorum:

def is_float(text):
    try:
        float(text)
        # check for nan/infinity etc.
        if text.isalpha():
            return False
        return True
    except ValueError:
        return False

1

Bu kod regex kullanarak üsleri, kayan sayıları ve tam sayıları işler.

return True if str1.lstrip('-').replace('.','',1).isdigit() or float(str1) else False

1

Kullanıcı yardımcı işlevi:

def if_ok(fn, string):
  try:
    return fn(string)
  except Exception as e:
    return None

sonra

if_ok(int, my_str) or if_ok(float, my_str) or if_ok(complex, my_str)
is_number = lambda s: any([if_ok(fn, s) for fn in (int, float, complex)])

0

Doğru ve Yanlış'dan daha yararlı değerler döndürerek istisna tekniğini kullanışlı bir şekilde genelleştirebilirsiniz. Örneğin, bu işlev tırnak işaretleri dizeler koyar, ancak sayıları yalnız bırakır. Hangi hızlı ve kirli bir filtre R için bazı değişken tanımları yapmak için gereken sadece budur.

import sys

def fix_quotes(s):
    try:
        float(s)
        return s
    except ValueError:
        return '"{0}"'.format(s)

for line in sys.stdin:
    input = line.split()
    print input[0], '<- c(', ','.join(fix_quotes(c) for c in input[1:]), ')'

0

Beni bu iş parçacığı, yani dizeleri ve numaraları en sezgisel bir şekilde bir koleksiyon dönüştürmek için neden bir sorun üzerinde çalışıyordu. Orijinal kodu okuduktan sonra ihtiyacım olanın iki şekilde farklı olduğunu fark ettim:

1 - Dize bir tamsayıyı temsil ediyorsa tam sayı sonucu istedim

2 - Bir sayı veya dize sonucunun veri yapısına yapışmasını istedim

bu yüzden bu türevi üretmek için orijinal kodu uyarladım:

def string_or_number(s):
    try:
        z = int(s)
        return z
    except ValueError:
        try:
            z = float(s)
            return z
        except ValueError:
            return s


0
def is_float(s):
    if s is None:
        return False

    if len(s) == 0:
        return False

    digits_count = 0
    dots_count = 0
    signs_count = 0

    for c in s:
        if '0' <= c <= '9':
            digits_count += 1
        elif c == '.':
            dots_count += 1
        elif c == '-' or c == '+':
            signs_count += 1
        else:
            return False

    if digits_count == 0:
        return False

    if dots_count > 1:
        return False

    if signs_count > 1:
        return False

    return True
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.