1,358


248

文字列が数値(float)かどうかを調べるにはどうすればいいですか?

Pythonで文字列が数値として表現できるかどうかを確認するための最善の方法は何ですか?

私が現在持っている機能は:

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

これは、醜くて遅いだけでなく、不格好なようです。 しかし、main関数で `float`を呼び出すのはさらに悪いので、私はもっと良い方法を見つけていません。

32 Answer


1,421


浮動小数点数ではなく(正の符号なし)整数の解析を探している場合は、https://docs.python.org/2/library/stdtypes.html#str.isdigit [isdigit()]関数を使用できます。文字列オブジェクトの場合。

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

Unicode文字列についてもありますが、私はhttps://docs.python.org/2/library/stdtypes.html#unicode.isnumeric[Unicodeにはあまり慣れていません。 - 10進数/ 10進数]


609


_ 、くて遅いだけでなく _

私は両方とも争います。

正規表現や他の文字列の解析は、より曖昧で遅くなります。

私は何かが上記よりも速くなることができるかどうかわからない。 関数を呼び出して戻ります。 Try / Catchでは、スタックフレームを広範囲に検索しなくても最も一般的な例外が発生するため、あまりオーバーヘッドが発生しません。

問題は、どの数値変換関数にも2種類の結果があることです。

  • 数字が有効な場合は数字

  • 有効でないことを示すステータスコード(たとえば、errno経由)または例外 番号を解析できました。

C(例として)は、これを回避するためのいくつかの方法があります。 Pythonはそれを明確かつ明示的にレイアウトしています。

これを行うためのコードは完璧だと思います。


96


  • TL; DR *最善の解決策は `s.replace( '。'、 ''、1).isdigit()`です

私はいくつかのhttp://nbviewer.ipython.org/github/rasbt/One-Python-benchmark-per-day/blob/master/ipython_nbs/day6_string_is_number.ipynb?create=1 [ベンチマーク]さまざまなアプローチを比較しました

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

文字列が数値ではない場合、except-blockはかなり遅いです。 しかしもっと重要なことに、try-exceptメソッドは科学的表記法を正しく扱う唯一のアプローチです。

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

浮動小数点表記「.1234」は、以下ではサポートされていません。 - 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__)

科学表記 "1.000000e 50"は、以下によってサポートされていません。 - is_number_regex - is_number_repl_isdigit
科学表記法「1e50」は、以下ではサポートされていません+ - is_number_regex - is_number_repl_isdigit

編集:ベンチマーク結果

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

以下の機能がテストされた場所

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


66


考慮しなければならない可能性がある例外が1つあります。文字列 'NaN’です。

is_numberが 'NaN’に対してFALSEを返すようにしたい場合、Pythonはそれを数字ではない数字の表現に変換するので機能しません(アイデンティティの問題について話してください):

>>>フロート( 'NaN')nan

さもなければ、私が実際に私が今広く使用しているコードの部分をありがとうございます。 :)

G.


53


これはどう:

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

これは、 '。'が1つあるかない場合にのみtrueを返します。数字のストリングで。

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

falseを返します

編集:ちょうど別のコメントを見た…​ 他の場合には `.replace(badstuff、 ''、maxnum_badstuff)`を追加することもできます。 もしあなたがsaltを渡していて、任意の調味料ではないならば(ref:http://xkcd.com/974/ [xkcd#974])これはうまくいくでしょう:P


39


Alfeが複雑なハンドルの両方でfloatをチェックする必要はないと指摘した後に更新されました。

def is_number(s):try:complex(s)#int、long、float、complexの場合はValueErrorを除き、falseを返します。

Trueを返す

'' '' '

以前に言われました:あなたがまた複雑な数をチェックする必要があるかもしれないいくつかのまれなケースです(例えば。 1 2i)、これはfloatで表すことはできません。

def is_number(s):try:float(s)#int、long、float(ValueError以外)の場合:try:complex(s)#ValueError以外の場合:return False

Trueを返す


37


_ これは、醜くて遅いだけでなく、不格好なようです。 _

慣れるまでに時間がかかるかもしれませんが、これがピトニックな方法です。 すでに指摘したように、代替案はもっと悪いです。 しかし、このようにすることにはもう1つの利点があります。それは多相です。

アヒルのタイピングの背後にある中心的な考え方は、「アヒルのように歩き、話すならば、それはアヒルだ」ということです。何かをfloatに変換できるかどうかを判断する方法を変更できるように、stringをサブクラス化する必要があると判断した場合はどうしますか? あるいは、他のオブジェクトを完全にテストすることにした場合はどうなりますか? 上記のコードを変更しなくても、これらのことを実行できます。

他の言語はインターフェースを使用することによってこれらの問題を解決します。 どのソリューションが別のスレッドに適しているかについての分析は省きます。 ただし、重要なのは、Pythonは方程式のアヒルタイピング側にあることです。Pythonで多くのプログラミングを行う予定がある場合は、おそらくこのような構文に慣れる必要があるでしょう(ただし、それは意味ではありません)。あなたはもちろんそれを好む必要があります。

考慮すべきもう1つのことがあります。Pythonは、他の多くの言語と比較して、例外のスローとキャッチが非常に高速です(たとえば、.Netよりも30倍高速です)。 一体、言語自体も例外をスローして、例外ではない通常のプログラム条件を伝達します(forループを使用するたびに)。 したがって、重大な問題に気付くまでは、このコードのパフォーマンス面についてあまり気にする必要はありません。


19


`int`にはこれを使います。

>>> "1221323".isdigit()
True

しかし `float`にはいくつかのトリックが必要です;-)。 すべての浮動小数点数は1つの点を持ちます…​

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

負の数の場合も同様に `lstrip()`を追加するだけです。

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

そして今、私たちは普遍的な方法を手に入れます。

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


15


Just C#をまねる

C#には、スカラー値の解析を処理する2つの異なる関数があります。

  • Float.Parse()

  • Float.TryParse()

  • float.parse():*

例外parse(string):try:return float(string)を除いて例外:throw TypeError

_注:例外をTypeErrorに変更した理由がわからない場合は、http://docs.python.org/library/exceptions.html [ここに文書があります]。

  • float.try_parse():*

def try_parse(string、fail =なし):try:return float(string)を除き、Exception:return failを返します。

_注:ブール値の 'False’を返したくはありません。これはまだ値型なのです。 それは失敗を示すため、どれも優れていません。 もちろん、何か違うものが欲しいなら、failパラメータを好きなものに変更することができます。

parse()とtry_parse()を含むようにfloatを拡張するには、これらのメソッドを追加するためにfloatクラスをモンキーパッチする必要があります。

既存の関数を尊重したい場合は、コードは次のようになります。

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

_SideNote:私が個人的にモンキーパンチングと呼ぶのを好みます、なぜなら私がこれをするとき、私は言語を濫用しているように感じますが、YMMV。

使用法:

float.parse( 'giggity')// throws TypeException float.parse('54 .3 ')//スカラ値54.3を返すfloat.tryParse(' twank ')//戻り値なしfloat.tryParse('32 .2')//戻り値スカラー値32.2

_そして偉大な賢者Pythonasは、聖座Sharpisusに言った、 "あなたができることなら何でも私はもっとよくできます。私はあなたよりももっと良いことができます"。


15


数字以外の文字列の場合、 `try:except:`は実際には正規表現より遅くなります。 有効な数値の文字列の場合、正規表現は遅くなります。 したがって、適切な方法は入力内容によって異なります。

自分がパフォーマンスの限界にあることがわかった場合は、https://pypi.org/project/fastnumbers[fastnumbers]という新しいサードパーティ製モジュールを使用して、http://fastnumbers.readthedocs.io/という名前の関数を使用できます。 ja / master / api.html#fastnumbers.isfloat [isfloat]。 完全な開示、私は作者です。 その結果を以下のタイミングに含めました。

'' '' '

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

ご覧のように

  • `try:except:`は数値入力では高速でしたが、無効な場合は非常に低速でした 入力

  • 入力が無効な場合、正規表現は非常に効率的です。

  • どちらの場合も `fastnumbers`が勝ちます