3

I read this question

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

but when using the following code I get a false on an np.array as can be demonstrated below.

import collections

isinstance(np.arange(10), collections.Sequence)
# returns false

I find it a bit annoying that I can't do len(1) and simply get 1.

The only work around I can think of is a try except statement such as the following:

a = 1
try:
    print len(a)
except TypeError:
    print 1

Is there a more Pythonic way to do this?

Community
  • 1
  • 1
evan54
  • 3,585
  • 5
  • 34
  • 61
  • 3
    At the *very least* qualify your `except`. Only `TypeError` needs to be caught here. How did you end up with a situation where you need got such a mix of types in the first place however? – Martijn Pieters Oct 23 '14 at 16:15
  • Anything wrong with the second answer in your link, which specifically mentions numpy? – Mark Ransom Oct 23 '14 at 16:24
  • @ Mark Ranson hm. I think it probably works. I was hoping that someone would say oh sure there is something like np.len(a) which does this kind of thing. It seems there isn't. – evan54 Oct 23 '14 at 21:14
  • @MartijnPieters well it's due to using `numpy`, `mpmath` or a single number. So the set of types it can be is finite (of course) but still would be nice not to list what happens under each case. – evan54 Oct 23 '14 at 21:15
  • @MartijnPieters edited the question as per your suggestion – evan54 Oct 23 '14 at 21:22

3 Answers3

8

collections.Sequence only applies to sequence objects, which are a very specific type of iterable object. Incidentally, a numpy.ndarray (which is returned by numpy.arange) is not a sequence.

You need to test for either collections.Iterable, which represents any iterable object:

>>> isinstance([1, 2, 3], collections.Iterable)
True
>> isinstance(np.arange(10), collections.Iterable)
True
>>> isinstance(1, collections.Iterable)
False
>>>

or collections.Sized, which represents any object that works with len:

>>> isinstance([1, 2, 3], collections.Sized)
True
>>> isinstance(np.arange(10), collections.Sized)
True
>>> isinstance(1, collections.Sized)
False
>>>

You can then use a conditional expression or similar to do what you want:

print len(a) if isinstance(a, collections.Iterable) else 1

print len(a) if isinstance(a, collections.Sized) else 1

For a complete list of the available abstract base classes in the collections module, see Collections Abstract Base Classes in the Python docs.

  • If you just want to know whether `len` will work or not, `collections.Sized` would be more general. But it depends what the OP actually wants to do with the objects. – Peter DeGlopper Oct 23 '14 at 16:20
  • I think I'll go with `collections.Sized` seems more general. Kind of surprised that this kind of function doesn't already exist in `numpy` or smth similar. – evan54 Oct 23 '14 at 21:08
  • Better use `collections.abc.Sized` now: `DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated since Python 3.3, and in 3.10 it will stop working.` – Martin Jun 11 '22 at 05:36
5

I'll just throw in another potential option:

length = getattr(obj, '__len__', lambda:1)()

So get either the __len__ method from the object, or a function that always returns 1, then call it to get your result.

I wouldn't say it's Pythonic, but avoids an import and exception handling. However, I'd still go with comparing if it's a collections.Sized and a conditional statement and put it in a helper function called len_or_1 or something.

Jon Clements
  • 138,671
  • 33
  • 247
  • 280
1

Although this isn't pythonic as it uses numpy here is another neat way to make this work:

import numpy as np
a = 1
aSh = np.shape(a)
if len(aSh) == 0:
    print 1
else:
    print max(aSh)

which gives a behaviour that should work with scalars, lists and matrices.

evan54
  • 3,585
  • 5
  • 34
  • 61