400

I have a function that takes the argument NBins. I want to make a call to this function with a scalar 50 or an array [0, 10, 20, 30]. How can I identify within the function, what the length of NBins is? or said differently, if it is a scalar or a vector?

I tried this:

>>> 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()
>>> 

As you see, I can't apply len to P, since it's not an array.... Is there something like isarray or isscalar in python?

thanks

otmezger
  • 10,410
  • 21
  • 64
  • 90

15 Answers15

539
>>> import collections.abc
>>> isinstance([0, 10, 20, 30], collections.abc.Sequence)
True
>>> isinstance(50, collections.abc.Sequence)
False

note: isinstance also supports a tuple of classes, check type(x) in (..., ...) should be avoided and is unnecessary.

You may also wanna check not isinstance(x, (str, unicode))

As noted by @2080 and also here this won't work for numpy arrays. eg.

>>> import collections.abc
>>> import numpy as np
>>> isinstance((1, 2, 3), collections.abc.Sequence)
True
>>> isinstance(np.array([1, 2, 3]), collections.abc.Sequence)
False

In which case you may try the answer from @jpaddison3:

>>> hasattr(np.array([1, 2, 3]), "__len__")
True
>>> hasattr([1, 2, 3], "__len__")
True
>>> hasattr((1, 2, 3), "__len__")
True

However as noted here, this is not perfect either, and will incorrectly (at least according to me) classify dictionaries as sequences whereas isinstance with collections.abc.Sequence classifies correctly:

>>> hasattr({"a": 1}, "__len__")
True
>>> from numpy.distutils.misc_util import is_sequence
>>> is_sequence({"a": 1})
True
>>> isinstance({"a": 1}, collections.abc.Sequence)
False

You could customise your solution to something like this, add more types to isinstance depending on your needs:

>>> isinstance(np.array([1, 2, 3]), (collections.abc.Sequence, np.ndarray))
True
>>> isinstance([1, 2, 3], (collections.abc.Sequence, np.ndarray))
True
jamylak
  • 128,818
  • 30
  • 231
  • 230
  • 3
    thanks, I didn't imagine inverting `list` to get false for scalars... thanks – otmezger May 29 '13 at 06:39
  • 8
    While this is a great answer, `collections.Sequence` is an ABC for string as well, so that should be taken into account. I'm using something like `if type(x) is not str and isinstance(x, collections.Sequence):`. This isn't great, but it is reliable. – bbenne10 Aug 04 '14 at 19:47
  • 3
    @bbenne10 sure, but avoid `type`, and also check `not isinstance(x, (str, unicode))` on Python 2 – jamylak Feb 10 '15 at 11:04
  • Why did you say "check type(x) in (..., ...) should be avoided and is unnecessary."? If you say so, that would be very kind to explain why, maybe I'm not the only one to wonder why it should be avoided. – Olivier Pons May 30 '17 at 07:50
  • @OlivierPons https://stackoverflow.com/questions/1549801/differences-between-isinstance-and-type-in-python – jamylak May 30 '17 at 14:52
  • The answer is ok for checking for an array, but not for validating that a variable is a scalar, unless Mapping like dict are considered scalar since `not isinstance({"a", 1, "b":3}, (list, collection.Sequence))` would return true which does not mean that the variable (here a dict) is a scalad. – Christian O'Reilly Dec 06 '19 at 22:12
  • 1
    `collections.Sequence` --> `collections.abc.Sequence` [may](https://mail.python.org/archives/list/python-dev@python.org/thread/EYLXCGGJOUMZSE5X35ILW3UNTJM3MCRE/) be required in Python 3.9 or 3.10. – Bob Stein Aug 19 '20 at 11:15
  • This won't work for numpy arrays – 2080 Jul 17 '22 at 22:58
  • 2
    unfortunately, `isinstance(np.array(1), (collections.abc.Sequence, np.ndarray))` (i.e., a numpy scalar) returns `True`, but `np.array(1)[0]` is an `IndexError` – scott Mar 10 '23 at 16:01
169

Previous answers assume that the array is a python standard list. As someone who uses numpy often, I'd recommend a very pythonic test of:

if hasattr(N, "__len__")
jpaddison3
  • 1,916
  • 1
  • 11
  • 10
77

Combining @jamylak and @jpaddison3's answers together, if you need to be robust against numpy arrays as the input and handle them in the same way as lists, you should use

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

This is robust against subclasses of list, tuple and numpy arrays.

And if you want to be robust against all other subclasses of sequence as well (not just list and tuple), use

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

Why should you do things this way with isinstance and not compare type(P) with a target value? Here is an example, where we make and study the behaviour of NewList, a trivial subclass of list.

>>> 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

Despite x and y comparing as equal, handling them by type would result in different behaviour. However, since x is an instance of a subclass of list, using isinstance(x,list) gives the desired behaviour and treats x and y in the same manner.

scottclowe
  • 2,015
  • 19
  • 20
  • 3
    This is the answer that most suited my needs. I just added set, too. Because I don't want to be robust against dicts. `isinstance(P, (list, tuple, set, np.ndarray))` – Santiago Feb 09 '20 at 15:53
64

Is there an equivalent to isscalar() in numpy? Yes.

>>> np.isscalar(3.1)
True
>>> np.isscalar([3.1])
False
>>> np.isscalar(False)
True
>>> np.isscalar('abcd')
True
Stefan
  • 919
  • 2
  • 13
  • 24
jmhl
  • 1,645
  • 12
  • 11
  • 7
    It would be better and an example :`>>> np.isscalar('abcd')` returns `True`. – Syrtis Major Mar 19 '16 at 08:03
  • 1
    thanks! this is a much more general example than any of the above and should be preferred. It's also a direct answer to the OP's question. – Cristóbal Sifón Jun 24 '18 at 04:28
  • 3
    Nice. Although one gotcha is that isscalar(None) returns False. Numpy implements this as `return (isinstance(num, generic) or type(num) in ScalarType or isinstance(num, numbers.Number))` – Shital Shah Jan 08 '19 at 20:24
  • 8
    **No, sadly.** The `numpy.isscalar()` function suffers a number of irreconcilable design flaws and will *probably* be deprecated at some future revision. To paraphrase [official documentation](https://docs.scipy.org/doc/numpy/reference/generated/numpy.isscalar.html): "In almost all cases `np.ndim(x) == 0` should be used instead of `np.isscaler(x)`, as the former will also correctly return true for 0d arrays." A robust forward-compatible alternative to `numpy.isscalar()` would thus be to trivially wrap `numpy.ndim()`: e.g., `def is_scalar(obj): return np.ndim(obj) == 0` – Cecil Curry Feb 28 '19 at 03:55
  • Actually this should not be upvoted because `np.isscalar` is confusing. Official doc suggested using `np.array.ndim` everywhere, i.e. `np.isscalar(np.array(12))` is False while it should be considered as scalar since `np.array(12).ndim` is 0. – knh190 Jun 04 '19 at 07:15
  • It doesn't work on mappings. `np.ndim({'a':'b', 'c':'d'}) == 0` – David Sauter Nov 06 '20 at 23:11
31

While, @jamylak's approach is the better one, here is an alternative approach

>>> N=[2,3,5]
>>> P = 5
>>> type(P) in (tuple, list)
False
>>> type(N) in (tuple, list)
True
Sukrit Kalra
  • 33,167
  • 7
  • 69
  • 71
7

Another alternative approach (use of class name property):

N = [2,3,5]
P = 5

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

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

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

No need to import anything.

Marek
  • 862
  • 7
  • 19
  • 2
    There's no advantage to doing this than just `type(N) is list` or `type(N) is int`. And as other answers have mentioned, doing a strict type equality check is usually less preferable to an `isinstance()` check, which can account for subclasses. – Aaron D Oct 21 '22 at 10:12
4

Here is the best approach I have found: Check existence of __len__ and __getitem__.

You may ask why? The reasons includes:

  1. The popular method isinstance(obj, abc.Sequence) fails on some objects including PyTorch's Tensor because they do not implement __contains__.
  2. Unfortunately, there is nothing in Python's collections.abc that checks for only __len__ and __getitem__ which I feel are minimal methods for array-like objects.
  3. It works on list, tuple, ndarray, Tensor etc.

So without further ado:

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

Note that I've added default parameters because most of the time you might want to consider strings as values, not arrays. Similarly for tuples.

Shital Shah
  • 63,284
  • 17
  • 238
  • 185
  • This doesn't work well on scalar (TensorFlow) tensors, because they have a __len__ method but raise an error if you try to call it on a scalar tensor: TypeError: Scalar tensor has no `len()`. Kind of annoying behaviour on the part of TensorFlow... – Ben Farmer Jul 23 '20 at 05:12
  • To deal with this I find myself first doing something like if hasattr(obj,"shape") and obj.shape==() to check for these "scalar array" cases. – Ben Farmer Jul 23 '20 at 05:37
4

To answer the question in the title, a direct way to tell if a variable is a scalar is to try to convert it to a float. If you get TypeError, it's not.

N = [1, 2, 3]
try:
    float(N)
except TypeError:
    print('it is not a scalar')
else:
    print('it is a scalar')
Puck
  • 2,080
  • 4
  • 19
  • 30
  • 3
    Is anything wrong with this answer? The chosen answer fails when doing `isinstance(np.arange(10), collections.Sequence)`. – Stefano Aug 04 '20 at 20:43
3
>>> N=[2,3,5]
>>> P = 5
>>> type(P)==type(0)
True
>>> type([1,2])==type(N)
True
>>> type(P)==type([1,2])
False
suhailvs
  • 20,182
  • 14
  • 100
  • 98
2

You can check data type of variable.

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

It will give you out put as data type of P.

<type 'int'>

So that you can differentiate that it is an integer or an array.

unnati patil
  • 1,601
  • 2
  • 13
  • 12
2

I am surprised that such a basic question doesn't seem to have an immediate answer in python. It seems to me that nearly all proposed answers use some kind of type checking, that is usually not advised in python and they seem restricted to a specific case (they fail with different numerical types or generic iteratable objects that are not tuples or lists).

For me, what works better is importing numpy and using array.size, for example:

>>> 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

Note also:

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

Out[5]: 2

but:

>>> 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
Vincenzooo
  • 2,013
  • 1
  • 19
  • 33
  • 1
    I'm also surprised that none of them seem to deal with generators either. – RhysC Oct 12 '16 at 07:41
  • It also doesn't work on mappings: `>>> np.array({1:2, 3:4}).size == 1` – David Sauter Nov 06 '20 at 23:13
  • 1
    these are because the np.array function creates an array of dtype `object`, with a single element containing the dictionary (or the generator). It is different using `np.array(list(a.items())).size` or `np.array(list(a.keys())).size` gives a different result. – Vincenzooo Nov 08 '20 at 18:53
2

Simply use size instead of len!

>>> from numpy import size
>>> N = [2, 3, 5]
>>> size(N)
3
>>> N = array([2, 3, 5])
>>> size(N)
3
>>> P = 5
>>> size(P)
1
  • 3
    NameError: name 'size' is not defined – thang Nov 11 '16 at 20:43
  • 1
    That's true. I was using numpy size without noticing it. You need: from numpy import size – Mathieu Villion Dec 04 '16 at 09:55
  • 5
    `np.size(5)` and `np.size([5])` are both == `1`, so this doesn't correctly distinguish type (i.e., identify a scalar), which I believe is the goal. – michael Jan 27 '17 at 13:14
  • This is an interesting remark. Original question refers to isscalar, which is a Matlab function. In Matlab, there is absolutely no difference between a scalar and an array of size 1, may it be a vector or a N-dim array. IMHO, this is a plus for Matlab. – Mathieu Villion Feb 05 '17 at 00:18
  • Madness. That would mean `{} == {{}}`. – John P May 14 '21 at 10:17
1

Since the general guideline in Python is to ask for forgiveness rather than permission, I think the most pythonic way to detect a string/scalar from a sequence is to check if it contains an integer:

try:
    1 in a
    print('{} is a sequence'.format(a))
except TypeError:
    print('{} is a scalar or string'.format(a))
Nicola
  • 81
  • 3
0

preds_test[0] is of shape (128,128,1) Lets check its data type using isinstance() function isinstance takes 2 arguments. 1st argument is data 2nd argument is data type isinstance(preds_test[0], np.ndarray) gives Output as True. It means preds_test[0] is an array.

0

You can easily use function isinstance(object, classinfo) in Python.

>>> isinstance(5, list)
False

>>>  isinstance([2, 3, 5], list)
True

See ducomentation for this function.

SayeMarg
  • 11
  • 3