入力引数がスカラーかどうかを簡単に判定したいとき,matlabだとisscalarという関数があるが,pythonの場合,どうするのがよさそうかわからなかったので調べた。
バージョン情報
こういった話はバージョン依存性があるので,本記事作成時のバージョンを残しておく。
- python 3.7.3
- numpy 1.17.0
方法1: Numberクラスを継承しているかどうかで判定
を読むと,こんなことが書かれている。
class numbers.Number
数の階層の根。引数 x が、種類は何であれ、数であるということだけチェックしたい場合、isinstance(x, Number) が使えます。
コードにすると,こんな感じ。シンプルでよい。
import numbers
def is_scalar(x):
return isinstance(x, numbers.Number)
方法2 : numpyの判定関数を使う
調べてみると,numpyにnumpy.isscalar
という関数があった。仕様はこのあたりが見やすい。
ソースコード上の定義箇所はここだった。コードを見る限り,方法1よりもスカラーと判定する範囲が確実に広い。関数コメントを見る限り,strとかもスカラーとする仕様になっている(個人的にはちょっと違和感がある。strが係数になるベクトル空間とか作れるのか?)
スカラー判定の範囲
よく使いそうなオブジェクトで試したので,一応載せておく。試した中では,str
とbytes
が方法1だとFalse,方法2(numpy.isscalar)だとTrueになり,それ以外は判定結果が一致した。実装時はこのあたりの違いに気を付けたほうがよさそう。
scalar_test.py
import numpy
import numbers
import decimal
def is_scalar(x):
return isinstance(x, numbers.Number)
def check(input_var):
print("=========================")
print(input_var)
print(type(input_var))
print("is_sclar_1: " + str(is_scalar(input_var)))
print("is_sclar_2: " + str(numpy.isscalar(input_var)))
if __name__ == "__main__":
# 方法1,2ともにスカラーと判定
var_int = 1
var_bool = True
var_float = 1.0
var_complex = 1 + 1j
var_npint32 = numpy.int32(1)
var_decimal = decimal.Decimal('12.345')
# 方法1はスカラー,方法2は非スカラーと判定
var_bytes_1 = bytes(1)
var_bytes_hoge = b'hoge'
var_str1 = "1"
var_str4 = "hoge"
# 方法1,2ともに非スカラーと判定
var_nan = None
var_nparray = numpy.array(1)
var_list = [1]
var_taple = (1,2) #(1)ならtapleにならないのでスカラーと判定される
var_dict = {"a": 1}
var_set = {1}
# 検証
check(var_int)
check(var_bool)
check(var_float)
check(var_complex)
check(var_npint32)
check(var_decimal)
check(var_bytes_1)
check(var_bytes_hoge)
check(var_str1)
check(var_str4)
check(var_nan)
check(var_nparray)
check(var_list)
check(var_taple)
check(var_dict)
check(var_set)
出力結果
=========================
1
<class 'int'>
is_sclar_1: True
is_sclar_2: True
=========================
True
<class 'bool'>
is_sclar_1: True
is_sclar_2: True
=========================
1.0
<class 'float'>
is_sclar_1: True
is_sclar_2: True
=========================
(1+1j)
<class 'complex'>
is_sclar_1: True
is_sclar_2: True
=========================
1
<class 'numpy.int32'>
is_sclar_1: True
is_sclar_2: True
=========================
12.345
<class 'decimal.Decimal'>
is_sclar_1: True
is_sclar_2: True
=========================
b'\x00'
<class 'bytes'>
is_sclar_1: False
is_sclar_2: True
=========================
b'hoge'
<class 'bytes'>
is_sclar_1: False
is_sclar_2: True
=========================
1
<class 'str'>
is_sclar_1: False
is_sclar_2: True
=========================
hoge
<class 'str'>
is_sclar_1: False
is_sclar_2: True
=========================
None
<class 'NoneType'>
is_sclar_1: False
is_sclar_2: False
=========================
1
<class 'numpy.ndarray'>
is_sclar_1: False
is_sclar_2: False
=========================
[1]
<class 'list'>
is_sclar_1: False
is_sclar_2: False
=========================
(1, 2)
<class 'tuple'>
is_sclar_1: False
is_sclar_2: False
=========================
{'a': 1}
<class 'dict'>
is_sclar_1: False
is_sclar_2: False
=========================
{1}
<class 'set'>
is_sclar_1: False
is_sclar_2: False