web-dev-qa-db-ja.com

オブジェクトが数値かどうかをチェックする最もPython的な方法は何ですか?

任意のpythonオブジェクトが与えられた場合、それが数値であるかどうかを判断する最善の方法は何ですか?ここでisacts like a number in certain circumstances

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

何かがintfloatlongboolであるかどうかを確認するのは面倒で、数字のように振る舞う可能性のあるユーザー定義のオブジェクトはカバーしません。しかし、__mul__は、たとえば、今説明したベクトルクラスが__mul__、しかしそれは私が望む種類の数字ではないだろう。

100
Claudiu

Number モジュールの numbers を使用して、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 '%15s %s' % (n.__repr__(), isinstance(n, Number))
              2 True
            2.0 True
 Decimal('2.0') True
         (2+0j) True
 Fraction(2, 1) True
            '2' False

もちろん、これはアヒルのタイピングに反しています。オブジェクトactsではなくオブジェクトisよりも心配な場合は、番号があるかのように操作を実行し、例外を使用してそうでないことを伝えます。

120

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

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

Python 2.5以前を使用している場合、唯一の現実的な方法は、これらの「特定の状況」の一部を確認して確認することです。

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

2.5以前では、「_0_に追加でき、反復不可能」が適切な定義になる場合があります。しかし、あなたは本当に、「数」を考慮したいことは間違いなくdoできなければならないこと、そして何をしなければならないのかを自問する必要があります絶対にunableである-確認してください。

これは2.6以降でも必要になる場合があります。おそらく、独自の登録を行って、_numbers.Numbers_にまだ登録されていない関心のある型を追加するためです。 exclude 数字であると主張するが処理できないタイプ、ABCにはunregisterメソッドがないため、さらに注意が必要です[たとえば、独自のABC WeirdNumを作成し、そこにすべて登録できます変なタイプの場合は、最初にisinstanceをチェックして解決してから、通常の_numbers.Number_のisinstanceをチェックしてから続行します。

ところで、xが何かをできるかできないかを確認する必要がある場合は、通常、次のようなことを試さなければなりません。

_try: 0 + x
except TypeError: canadd=False
else: canadd=True
_

___add___自体の存在は、たとえばすべてのシーケンスが他のシーケンスとの連結のためにそれを持っているため、何も有用ではありません。このチェックは、「数値は、そのようなもののシーケンスが組み込み関数sumの有効な単一引数であるようなもの」などの定義と同等です。完全に奇妙な型(たとえば、ZeroDivisionErrorValueError&cなど、合計が0になったときに「間違った」例外を発生させる型)は例外を伝播しますが、それで構いません。良い会社で;-);ただし、スカラーに加算可能な「ベクトル」(Pythonの標準ライブラリにはありませんが、もちろんサードパーティの拡張機能として人気があります)は、ここで間違った結果を与えるため、(たとえば)このチェックは after 「反復可能ではない」もの(たとえば、iter(x)TypeErrorを発生させることを確認するか、特別なメソッド___iter___の存在を確認する-if 2.5以前にいるため、独自のチェックが必要です)。

そのような複雑さを簡単に垣間見ると、実行可能な場合はいつでも抽象基本クラスに依存するように動機づけるのに十分かもしれません... ;-)。

30
Alex Martelli

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

しかし、明らかに、これは操作がworksであるかどうかだけをチェックし、理にかなっている!そのための唯一の本当の解決策は、タイプを決して混合せず、値がどのタイプクラスに属しているかを常に正確に知ることです。

16
Jochen Ritzel

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

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__を定義するいくつかの非数値オブジェクトがありますが、それは極端な例外です。このソリューションは、あなたが生成/誘導するすべてのnormalおよびsaneコードをカバーする必要があります。

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])
4
shrewmouse

質問を言い換えると、何かがコレクションなのか単一の値なのかを判断しようとしています。何かがベクトルなのか数字なのかを比較しようとすると、リンゴとオレンジが比較されます-文字列または数字のベクトルを持つことができ、単一の文字列または数字を持つことができます。 あなたが持っている数(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
Gordon Bean

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

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

2
sth

既存のメソッドを要約/評価するには:

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))
2
Martin Thoma

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

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

1
shadab.tughlaq

引数のタイプに応じて異なるメソッドを呼び出したい場合は、 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]
_

残念ながら、(私の知る限り)Vector型をまだ定義しているため、@dispatch(Vector)を記述できません。そのため、その型名はまだ定義されていません。代わりに、ベースタイプlistを使用しています。これにより、Vectorlistのドット積を見つけることもできます。

0
AJNeufeld

仮想ベクトルクラスの場合:

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

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

float(x)

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

0
Ant6n