任意のpythonオブジェクトが与えられた場合、それが数値であるかどうかを判断する最善の方法は何ですか?ここでis
はacts like a number in certain circumstances
。
たとえば、ベクトルクラスを記述しているとします。別のベクトルが与えられた場合、内積を求めます。スカラーが指定されている場合、ベクトル全体をスケーリングする必要があります。
何かがint
、float
、long
、bool
であるかどうかを確認するのは面倒で、数字のように振る舞う可能性のあるユーザー定義のオブジェクトはカバーしません。しかし、__mul__
は、たとえば、今説明したベクトルクラスが__mul__
、しかしそれは私が望む種類の数字ではないだろう。
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よりも心配な場合は、番号があるかのように操作を実行し、例外を使用してそうでないことを伝えます。
オブジェクトがあるかどうかを確認したい
特定の状況で数字のように振る舞う
Python 2.5以前を使用している場合、唯一の現実的な方法は、これらの「特定の状況」の一部を確認して確認することです。
2.6以降では、isinstance
を numbers.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
の有効な単一引数であるようなもの」などの定義と同等です。完全に奇妙な型(たとえば、ZeroDivisionError
やValueError
&cなど、合計が0になったときに「間違った」例外を発生させる型)は例外を伝播しますが、それで構いません。良い会社で;-);ただし、スカラーに加算可能な「ベクトル」(Pythonの標準ライブラリにはありませんが、もちろんサードパーティの拡張機能として人気があります)は、ここで間違った結果を与えるため、(たとえば)このチェックは after 「反復可能ではない」もの(たとえば、iter(x)
がTypeError
を発生させることを確認するか、特別なメソッド___iter__
_の存在を確認する-if 2.5以前にいるため、独自のチェックが必要です)。
そのような複雑さを簡単に垣間見ると、実行可能な場合はいつでも抽象基本クラスに依存するように動機づけるのに十分かもしれません... ;-)。
これは、例外が本当に輝く良い例です。数値型を使用して行うことを行い、他のすべてからTypeError
をキャッチします。
しかし、明らかに、これは操作がworksであるかどうかだけをチェックし、理にかなっている!そのための唯一の本当の解決策は、タイプを決して混合せず、値がどのタイプクラスに属しているかを常に正確に知ることです。
オブジェクトにゼロを掛けます。任意の回数のゼロはゼロです。その他の結果は、オブジェクトが数字ではないことを意味します(例外を含む)
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])
質問を言い換えると、何かがコレクションなのか単一の値なのかを判断しようとしています。何かがベクトルなのか数字なのかを比較しようとすると、リンゴとオレンジが比較されます-文字列または数字のベクトルを持つことができ、単一の文字列または数字を持つことができます。 あなたが持っている数(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]
最終的に、何かがスカラーに似ているかどうかをテストするよりも、何かがベクトルに似ているかどうかをテストする方が簡単です。異なるタイプの値(つまり、文字列、数値など)が通過している場合、プログラムのロジックには何らかの作業が必要になる可能性があります-最初に文字列に数値ベクトルを乗算しようとしたのはどうしてですか?
おそらく、逆の方法で行う方が良いでしょう。それがベクトルかどうかを確認します。もしそうなら、あなたはドット積を行い、他のすべての場合にはスカラー乗算を試みます。
ベクターのチェックは簡単です。ベクタークラスタイプ(またはそれから継承)である必要があるためです。また、最初にドット積を試してみて、それが失敗した場合(=実際にはベクトルではなかった場合)、スカラー乗算にフォールバックすることもできます。
既存のメソッドを要約/評価するには:
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))
追加するだけです。おそらく、次のようにisinstanceとisdigitの組み合わせを使用して、値が数値(int、floatなど)であるかどうかを調べることができます
isinstance(num1、int)またはisinstance(num1、float)またはnum1.isdigit()の場合:
引数のタイプに応じて異なるメソッドを呼び出したい場合は、 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
を使用しています。これにより、Vector
とlist
のドット積を見つけることもできます。
仮想ベクトルクラスの場合:
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 )
ある種のベクトルクラスを実装するときに、同様の問題がありました。番号をチェックする1つの方法は、単に1に変換することです。
float(x)
これは、xを数値に変換できない場合を拒否する必要があります。しかし、有効な他の種類の数値のような構造、たとえば複素数も拒否する場合があります。