オブジェクトが数値であるかどうかを確認する最もパイソン的な方法は何ですか?


114

任意のpythonオブジェクトが与えられた場合、それが数値であるかどうかを判断する最良の方法は何ですか?ここisでとして定義されacts like a number in certain circumstancesます。

たとえば、ベクトルクラスを記述しているとします。別のベクトルが指定されている場合は、内積を求めます。スカラーを指定した場合、ベクトル全体をスケーリングします。

何かがあるかどうかのチェックintfloatlongbool迷惑ですと数字のように振る舞うかもしれないユーザ定義のオブジェクトをカバーしていません。しかし、__mul__たとえばのチェックは、今説明したベクタークラスがを定義するので十分__mul__ではありませんが、必要な数の種類ではありません。

回答:


135

モジュールから使用Numbernumbersてテストしますisinstance(n, Number)(2.6以降で使用可能)。

>>> from numbers import Number
... from decimal import Decimal
... from fractions import Fraction
... for n in [2, 2.0, Decimal('2.0'), complex(2, 0), Fraction(2, 1), '2']:
...     print(f'{n!r:>14} {isinstance(n, Number)}')
              2 True
            2.0 True
 Decimal('2.0') True
         (2+0j) True
 Fraction(2, 1) True
            '2' False

もちろん、これはアヒルのタイピングとは逆です。オブジェクトどのように機能するかでなく、どのように機能するかが気になる場合は、数値があるかのように操作を実行し、例外を使用してそれ以外のことを伝えます。


3
ベクトルにXを掛けるときは、アヒルのことではなく、賢いことをすることをお勧めします。この場合、X 何であるかに基づいてさまざまなことを行います。(それは増殖する何かとして機能するかもしれませんが、結果は無意味かもしれません。)
Evgeni Sergeev

3
この答えは、True is a number ..であると言うでしょう。ブール値を除外する場合(検証feを考える)、私は言うでしょうisinstance(value, Number) and type(value) != bool
Yo Ludke 2017

32

オブジェクトがあるかどうかを確認したい

特定の状況では数字のように振る舞う

Python 2.5以前を使用している場合、実際の唯一の方法は、これらの「特定の状況」のいくつかをチェックして確認することです。

2.6以降ではisinstancenumbers.Numberを使用できます-この目的のために正確に存在する抽象基本クラス(ABC)(collectionsさまざまな形式のコレクション/コンテナーのモジュールには、2.6から始まる、さらに多くのABCが存在します)、およびまた、これらのリリースでのみ、必要に応じて独自の抽象基本クラスを簡単に追加できます)。

2.5以前のバッハは、「追加0可能で反復可能ではない」が適切な定義である場合があります。しかし、あなたは本当にそれはあなたが考慮したいと思うものを「数」は確かにすることができなければならないことを求めていることは何か、自問する必要が行う、絶対にする必要があり何ができないと確認する-ことができません。

これは、2.6以降でも必要になる可能性があります。おそらく、独自の登録を行って、まだ登録されていない気になるタイプを追加する目的で、それらが番号であると主張するタイプnumbers.Numbers除外したい場合は、 ABCにはunregister方法がないため、処理できないだけであり、さらに注意が必要です(たとえば、独自のABC WeirdNumを作成して、そのような奇妙なタイプをすべて登録し、次に、isinstance先に進む前にその異常終了を確認してください。isinstance通常のチェックにnumbers.Numberに続行ます。

ところで、x何かを実行できるかどうかを確認する必要がある場合は、通常、次のようなことを試す必要があります。

try: 0 + x
except TypeError: canadd=False
else: canadd=True

__add__たとえば、すべてのシーケンスが他のシーケンスとの連結のためにそれを持っているので、それ自体の存在は何の役にも立ちません。このチェックは、たとえば、「数値は、そのようなもののシーケンスが組み込み関数への有効な単一の引数であるようなものです」という定義と同等ですsum。完全に奇妙なタイプ(たとえば、a ZeroDivisionErrorValueError&c など、合計が0のときに「間違った」例外が発生するタイプ)は例外を伝播しますが、問題はありません。このようなクレイジーなタイプは単に許容できないことをユーザーにできるだけ早く伝えてください。会社;-); しかし、スカラーに加算可能な「ベクター」(Pythonの標準ライブラリにはありませんが、もちろんサードパーティの拡張機能として人気があります)でも、ここでは間違った結果が得られます(たとえば(つまり例えば、チェック1「反復可能であることを許されない」iter(x)昇給TypeError、または特別法の存在のために__iter__-あなたが2.5またはそれ以前のバージョンにしているので、あなた自身のチェックが必要な場合)。

このような複雑な問題を簡単に垣間見るだけで、可能であればいつでも、抽象基本クラスに依存するように動機づけることができます... ;-)。


しかし、数値モジュールには数値のABCがあります。それはドキュメントが主張するものです:「numbersモジュール(PEP 3141)は、より多くの演算を徐々に定義する数値抽象基底クラスの階層を定義します。」
Steven Rumbalski、2010

17

これは例外が本当に輝く良い例です。数値型で行うことを実行し、TypeError他のすべてからをキャッチします。

しかし、明らかに、これは操作が機能するかどうかのみをチェックし、意味があるかどうかはチェックしません!そのための唯一の実際の解決策は、型を決して混合せず、常に値がどの型クラスに属しているかを正確に把握することです。


1
+1 for Duck Typing:データの種類に関係なく、自分のやりたいことができるかどうかは関係ありません。
systempuntoout 2010

12
これは従来のアプローチでしたが、ABCは、純粋なアヒルのタイピングから離れて、多くの場合isinstanceに実際に役立つ可能性のある世界に距離を移すためにかなり導入されました(== "check it意義がある"および正式な適用性操作の)。長い間Pythonだけを使っている人にとっては難しいシフトですが、Pythonの哲学において非常に重要な微妙な傾向であり、無視することは重大なエラーとなるでしょう。
Alex Martelli、2010

@アレックス:真実であり、私はタイプクラスが大好きです(ほとんどcollections.Sequenceと友達)。しかし、残念ながら、数値、ベクトル、その他の数学的オブジェクトについては、そのようなクラスはありません。
Jochen Ritzel、2010

1
アヒルのタイピングに対して何もありません。それは私が何をするかです。しかし、数値の抽象基本クラス、numbers.Numberがあります。
Steven Rumbalski、2010

4

オブジェクトにゼロを乗算します。ゼロを掛けた数はゼロです。その他の結果は、オブジェクトが数値ではないことを意味します(例外を含む)

def isNumber(x):
    try:
        return bool(0 == x*0)
    except:
        return False

したがって、isNumberを使用すると、次の出力が得られます。

class A: pass 

def foo(): return 1

for x in [1,1.4, A(), range(10), foo, foo()]:
    answer = isNumber(x)
    print('{answer} == isNumber({x})'.format(**locals()))

出力:

True == isNumber(1)
True == isNumber(1.4)
False == isNumber(<__main__.A instance at 0x7ff52c15d878>)
False == isNumber([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
False == isNumber(<function foo at 0x7ff52c121488>)
True == isNumber(1)

__mul__ゼロを掛けるとゼロを返すように定義されている非数のオブジェクトが世界にはおそらく存在しますが、これは極端な例外です。このソリューションは、すべてカバーする必要があり、通常正気あなたが/ encouterを生成するコードを。

numpy.arrayの例:

import numpy as np

def isNumber(x):
    try:
        return bool(x*0 == 0)
    except:
        return False

x = np.array([0,1])

answer = isNumber(x)
print('{answer} == isNumber({x})'.format(**locals()))

出力:

False == isNumber([0 1])

5
True * 0 == 0
内部石2017

4
あなたの関数はブールが数値であると誤って言うでしょう
内部石17/07/27

1
@endolithでは、ブール値は数値とまったく同じように機能します。真は常に== 1で、偽は常に== 0です。これは、質問者が求めていたとおりです。
shrewmouse

1
@endolith、実際には、ブール値は数値です。ブール値はから派生するintので、私の関数はブール値は数値であると正しく述べます。
shrewmouse 2017

1
@ NicolasAbril、0 * x == 0をisNumber内のブール値に変換します。
shrewmouse

3

質問を言い換えると、何かがコレクションなのか単一の値なのかを判断しようとしています。何かがベクトルか数値かを比較しようとすると、リンゴとオレンジが比較されます。文字列または数値のベクトルを使用でき、単一の文字列または数値を使用できます。実際に持っているタイプではなく、持っている数(1以上)興味があります。

この問題に対する私の解決策は、の存在を確認することにより、入力が単一の値であるかコレクションであるかを確認することです__len__。例えば:

def do_mult(foo, a_vector):
    if hasattr(foo, '__len__'):
        return sum([a*b for a,b in zip(foo, a_vector)])
    else:
        return [foo*b for b in a_vector]

または、アヒルのタイピングアプローチの場合は、foo最初に反復を試すことができます。

def do_mult(foo, a_vector):
    try:
        return sum([a*b for a,b in zip(foo, a_vector)])
    except TypeError:
        return [foo*b for b in a_vector]

最終的に、何かがスカラのようであるかどうかをテストするよりも、何かがベクトルのようであるかどうかをテストする方が簡単です。異なるタイプの値(文字列、数値など)が通過する場合、プログラムのロジックにいくつかの作業が必要になる場合があります。そもそも、文字列に数値ベクトルを掛けようとするのはどうしてですか。


3

既存の方法を要約/評価するには:

Candidate    | type                      | delnan | mat | shrewmouse | ant6n
-------------------------------------------------------------------------
0            | <type 'int'>              |      1 |   1 |          1 |     1
0.0          | <type 'float'>            |      1 |   1 |          1 |     1
0j           | <type 'complex'>          |      1 |   1 |          1 |     0
Decimal('0') | <class 'decimal.Decimal'> |      1 |   0 |          1 |     1
True         | <type 'bool'>             |      1 |   1 |          1 |     1
False        | <type 'bool'>             |      1 |   1 |          1 |     1
''           | <type 'str'>              |      0 |   0 |          0 |     0
None         | <type 'NoneType'>         |      0 |   0 |          0 |     0
'0'          | <type 'str'>              |      0 |   0 |          0 |     1
'1'          | <type 'str'>              |      0 |   0 |          0 |     1
[]           | <type 'list'>             |      0 |   0 |          0 |     0
[1]          | <type 'list'>             |      0 |   0 |          0 |     0
[1, 2]       | <type 'list'>             |      0 |   0 |          0 |     0
(1,)         | <type 'tuple'>            |      0 |   0 |          0 |     0
(1, 2)       | <type 'tuple'>            |      0 |   0 |          0 |     0

(私はこの質問でここに来ました)

コード

#!/usr/bin/env python

"""Check if a variable is a number."""

import decimal


def delnan_is_number(candidate):
    import numbers
    return isinstance(candidate, numbers.Number)


def mat_is_number(candidate):
    return isinstance(candidate, (int, long, float, complex))


def shrewmouse_is_number(candidate):
    try:
        return 0 == candidate * 0
    except:
        return False


def ant6n_is_number(candidate):
    try:
        float(candidate)
        return True
    except:
        return False

# Test
candidates = (0, 0.0, 0j, decimal.Decimal(0),
              True, False, '', None, '0', '1', [], [1], [1, 2], (1, ), (1, 2))

methods = [delnan_is_number, mat_is_number, shrewmouse_is_number, ant6n_is_number]

print("Candidate    | type                      | delnan | mat | shrewmouse | ant6n")
print("-------------------------------------------------------------------------")
for candidate in candidates:
    results = [m(candidate) for m in methods]
    print("{:<12} | {:<25} | {:>6} | {:>3} | {:>10} | {:>5}"
          .format(repr(candidate), type(candidate), *results))

TODO for myself:float('nan'), 'nan', '123.45', '42', '42a', '0x8', '0xa'、addmath.isnan
Martin Thoma

2

おそらく、逆の方法で行う方が良いでしょう。ベクトルかどうかを確認します。そうであれば、ドット積を行い、それ以外の場合はすべてスカラー倍算を試みます。

ベクターのチェックは、ベクタークラスタイプ(またはベクタークラスから継承)であるため、簡単です。最初にドット積を試してみて、それが失敗した場合(=それが実際にはベクトルではなかった場合)、スカラー乗算にフォールバックすることもできます。


1

追加するだけです。おそらく、次のようにisinstanceとisdigitの組み合わせを使用して、値が数値(int、floatなど)かどうかを調べることができます

isinstance(num1、int)またはisinstance(num1、float)またはnum1.isdigit()の場合:


0

架空のベクトルクラスの場合:

仮定vのベクトルであり、我々はを掛けていますx。それはそれぞれの構成要素掛けることは理にかなっている場合vによってはx、我々は、おそらく、そのため最初に試すことを意味しました。そうでなければ、たぶん、私たちは点在することができますか?それ以外の場合は、タイプエラーです。

EDIT -コードの下ので、ない仕事をする2*[0]==[0,0]代わりに上げますTypeError。コメントされたのでそのままにしておきます。

def __mul__( self, x ):
    try:
        return [ comp * x for comp in self ]
    except TypeError:
        return [ x * y for x, y in itertools.zip_longest( self, x, fillvalue = 0 )

場合x、ベクターは、その後[comp * x for comp in self]の外積もたらします。これは、ランク2のテンソルであり、スカラーではありません。xv
aaronasterling 2010

「スカラではない」を「ベクトルではない」に変更します。少なくとも元のベクトル空間にはありません。
aaronasterling 2010

ふむ、実は私たちは二人とも間違っている。あなたはそれcomp*xxによってスケーリングされるcompと仮定している、私はそれがTypeErrorを発生させると仮定していた。残念ながら、実際にxはそれ自体がcomp時間と連結します。おっとっと。
カトリエル2010

ええ。xベクトルの場合、__rmul__メソッド(__rmul__ = __mul__)がcomp * x必要です。これにより、意図したとおりにスケーリングxさ れるはずx * compです。
aaronasterling 2010

0

一種のベクトルクラスを実装するときに、同様の問題が発生しました。数をチェックする1つの方法は、1つに変換することです。つまり、

float(x)

これにより、xを数値に変換できない場合が拒否されます。ただし、有効な他の種類の数値のような構造、たとえば複素数も拒否する可能性があります。


0

引数のタイプに応じて異なるメソッドを呼び出したい場合は、を調べてくださいmultipledispatch

たとえば、ベクトルクラスを記述しているとします。別のベクトルが指定されている場合は、内積を求めます。スカラーを指定した場合、ベクトル全体をスケーリングします。

from multipledispatch import dispatch

class Vector(list):

    @dispatch(object)
    def __mul__(self, scalar):
        return Vector( x*scalar for x in self)

    @dispatch(list)
    def __mul__(self, other):
        return sum(x*y for x,y in zip(self, other))


>>> Vector([1,2,3]) * Vector([2,4,5])   # Vector time Vector is dot product
25
>>> Vector([1,2,3]) * 2                 # Vector times scalar is scaling
[2, 4, 6]

残念ながら、(私の知る限り)@dispatch(Vector)typeをまだ定義しているため、書き込むことができVectorません。そのため、type名はまだ定義されていません。代わりに、基本型listを使用しています。これにより、a Vectorとaの内積を見つけることもできますlist


0

短くて簡単な方法:

obj = 12345
print(isinstance(obj,int))

出力:

True

オブジェクトが文字列の場合、「False」が返されます。

obj = 'some string'
print(isinstance(obj,int))

出力:

False

0

データ項目rec_dayがあり、ファイルに書き込まれるとになりますfloat。ただし、プログラムの処理中はfloatintまたはのいずれかになりますstr(これstrは、新しいレコードを初期化するときに使用され、ダミーのフラグ値が含まれます)。

次に、これで番号があるかどうかを確認できます

                type(rec_day) != str 

私はこのようにpythonプログラムを構築し、これを数値チェックとして使用して「メンテナンスパッチ」を配置しました。Pythonicの方法ですか?ほとんどの場合、私がCOBOLでプログラミングしていたときからではありません。


-1

isdigit()関数を使用できます。

>>> x = "01234"
>>> a.isdigit()
True
>>> y = "1234abcd"
>>> y.isdigit()
False

「01234」は数字ではなく文字列です。
アレクセイ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.