python:如何识别变量是数组还是标量array or a scalar

python: how to identify if a variable is an array or a scalar

我有一个函数接受参数NBins。我想用标量50或数组[0, 10, 20, 30]调用这个函数。如何在函数内标识NBins的长度?或者换言之,如果它是一个标量或向量?

我试过这个:

1
2
3
4
5
6
7
8
9
>>> N=[2,3,5]
>>> P = 5
>>> len(N)
3
>>> len(P)
Traceback (most recent call last):
  File"<stdin>", line 1, in <module>
TypeError: object of type 'int' has no len()
>>>

如您所见,我不能将len应用于P,因为它不是数组…在python中是否有类似于isarrayisscalar的东西?

谢谢


1
2
3
4
>>> isinstance([0, 10, 20, 30], list)
True
>>> isinstance(50, list)
False

要支持任何类型的序列,请检查collections.Sequence,而不是list

注:isinstance也支持类的元组,检查type(x) in (..., ...)应该避免,不需要。

你也可以查一下not isinstance(x, (str, unicode))


前面的答案假设数组是一个Python标准列表。作为一个经常使用麻木药的人,我建议你做一个非常有助于以下方面的Python测试:

1
if hasattr(N,"__len__")


将@jamylak和@jpaddison3的答案组合在一起,如果需要对作为输入的numpy数组保持健壮并以与列表相同的方式处理它们,则应该使用

1
2
import numpy as np
isinstance(P, (list, tuple, np.ndarray))

对于列表、元组和numpy数组的子类,这是健壮的。

如果您还希望对序列的所有其他子类(不仅仅是列表和元组)具有健壮性,请使用

1
2
3
import collections
import numpy as np
isinstance(P, (collections.Sequence, np.ndarray))

你为什么要这样对待isinstance,而不把type(P)与目标值进行比较?下面是一个例子,我们在这里制作和研究了NewList的行为,EDOCX1是列表的一个小类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
>>> class NewList(list):
...     isThisAList = '???'
...
>>> x = NewList([0,1])
>>> y = list([0,1])
>>> print x
[0, 1]
>>> print y
[0, 1]
>>> x==y
True
>>> type(x)
<class '__main__.NewList'>
>>> type(x) is list
False
>>> type(y) is list
True
>>> type(x).__name__
'NewList'
>>> isinstance(x, list)
True

尽管xy比较相同,但由type处理会导致不同的行为。然而,由于xlist的一个子类的实例,使用isinstance(x,list)给出了所需的行为,并以相同的方式处理xy


numpy中是否有与isscalar()等效的函数?对。

1
2
3
4
5
6
>>> np.isscalar(3.1)
True
>>> np.isscalar([3.1])
False
>>> np.isscalar(False)
True


虽然,@jamilak的方法是更好的,但这里有一个替代方法

1
2
3
4
5
6
>>> N=[2,3,5]
>>> P = 5
>>> type(P) in (tuple, list)
False
>>> type(N) in (tuple, list)
True


只需使用size而不是len

1
2
3
4
5
6
7
8
9
10
>>> from numpy import size
>>> N = [2, 3, 5]
>>> size(N)
3
>>> N = array([2, 3, 5])
>>> size(N)
3
>>> P = 5
>>> size(P)
1


另一种替代方法(使用类名属性):

1
2
3
4
5
6
7
8
9
10
11
N = [2,3,5]
P = 5

type(N).__name__ == 'list'
True

type(P).__name__ == 'int'
True

type(N).__name__ in ('list', 'tuple')
True

不需要导入任何内容。


您可以检查变量的数据类型。

1
2
3
N = [2,3,5]
P = 5
type(P)

它会将输出作为P的数据类型。

1
<type 'int'>

这样就可以区分它是整数还是数组。


1
2
3
4
5
6
7
8
>>> N=[2,3,5]
>>> P = 5
>>> type(P)==type(0)
True
>>> type([1,2])==type(N)
True
>>> type(P)==type([1,2])
False

我很惊讶这样一个基本的问题在Python中似乎没有直接的答案。在我看来,几乎所有的建议答案都使用某种类型检查,这在Python中通常是不建议的,并且它们似乎仅限于特定的情况(它们失败于不同的数值类型或非元组或列表的通用可迭代对象)。

对我来说,更好的方法是导入numpy并使用array.size,例如:

1
2
3
4
5
6
7
8
9
10
11
12
>>> a=1
>>> np.array(a)
Out[1]: array(1)

>>> np.array(a).size
Out[2]: 1

>>> np.array([1,2]).size
Out[3]: 2

>>> np.array('125')
Out[4]: 1

注意事项:

1
2
3
>>> len(np.array([1,2]))

Out[5]: 2

但是:

1
2
3
4
5
6
7
>>> len(np.array(a))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-40-f5055b93f729> in <module>()
----> 1 len(np.array(a))

TypeError: len() of unsized object


这里是我发现的最佳方法:检查__len____getitem__的存在。

你可以问为什么?原因包括:

  • 这会检测到一些流行的有效数组对象,包括python的原生列表和tuple、numpy的ndarray和pytorch的张量。
  • 另一种流行的方法isinstance(obj, abc.Sequence)在一些对象上失败,包括pytorch张量,因为它们不实现__contains__
  • 使用collections.abc更可取,但不幸的是,python的collections.abc中没有只检查__len____getitem__的内容。
  • 所以不用再多费吹灰之力:

    1
    2
    3
    4
    5
    6
    7
    def is_array_like(obj, string_is_array=False, tuple_is_array=True):
        result = hasattr(obj,"__len__") and hasattr(obj, '__getitem__')
        if result and not string_is_array and isinstance(obj, (str, abc.ByteString)):
            result = False
        if result and not tuple_is_array and isinstance(obj, tuple):
            result = False
        return result

    请注意,我添加了默认参数,因为大多数时候您可能希望将字符串视为值,而不是数组。类似于元组。