How can I test if an argument is a sequence or a scalar?

S

sandravandale

I want to be able to pass a sequence (tuple, or list) of objects to a
function, or only one.

It's easy enough to do:

isinstance(var, (tuple, list))

But I would also like to accept generators. How can I do this?

Anything else is assumed to be a single value (my fault if I pass a
dict or something else to the function) and I make it into a tuple with
a single value (var, ) in order to simplify the algorithm (wasn't there
a special name for a one item tuple? Its been a while since I've
programmed in Python.)

Is there an easy way to do that?

Thanks,
-Sandra
 
S

sandravandale

Hi Jean-Paul,

Thank you for the advice, I appreciate it, my university training was
next to useless so I learn how to write good programs by the advice of
helpful people in online communities. I'm really thankfull when people
take the time to tell me when I'm doing something wrong and show me a
better way.

And I was still curious, so thank you for explaining how to distinguish
iterables from non-iterables.

Have a great day!

-Sandra
 
S

sandravandale

Hi Jean-Paul,

Thank you for the advice, I appreciate it, my university training was
next to useless so I learn how to write good programs by the advice of
helpful people in online communities. I'm really thankfull when people
take the time to tell me when I'm doing something wrong and show me a
better way.

And I was still curious, so thank you for explaining how to distinguish
iterables from non-iterables.

Have a great day!

-Sandra
 
M

Michael Spencer

Jean-Paul Calderone said:
....
> but in case you're curious, the easiest
way to tell an iterable from a non-iterable is by trying to iterate over
it. Actually, by doing what iterating over it /would/ have done:
....

You can usefully wrap up this test in a convenience function that always returns
an iterator (possibly over a zero length sequence):

One definition of safe_iter is below. Your definition may depend on the
specific application:

Cheers

Michael


def safe_iter(obj, atomic_types = (basestring, int, float, complex)):
"""Equivalent to iter when obj is iterable and not defined as atomic.
If obj is defined atomic or found to be not iterable, returns iter((obj,)).
safe_iter(None) returns an empty iterator"""
if not isinstance(obj, atomic_types):
try:
return iter(obj)
except TypeError:
pass
return iter((obj,) * (obj is not None))

def test_safe_iter():
assert list(safe_iter(1)) == [1]
assert list(safe_iter("string")) == ["string"]
assert list(safe_iter(range(10))) == range(10)
assert list(safe_iter(xrange(10))) == list(xrange(10))
assert list(safe_iter((1,2,3))) == [1,2,3]
assert list(safe_iter(1.0)) == [1.0]
assert list(safe_iter(1+2j)) == [1+2j]
xiter = iter(range(10))
assert safe_iter(xiter) is xiter
xiter = (a for a in range(10))
assert safe_iter(xiter) is xiter
assert list(safe_iter(None)) == []
 
R

Russell E. Owen

I want to be able to pass a sequence (tuple, or list) of objects to a
function, or only one.

It's easy enough to do:

isinstance(var, (tuple, list))

But I would also like to accept generators. How can I do this?

Anything else is assumed to be a single value (my fault if I pass a
dict or something else to the function) and I make it into a tuple with
a single value (var, ) in order to simplify the algorithm (wasn't there
a special name for a one item tuple? Its been a while since I've
programmed in Python.)

Is there an easy way to do that?

Here is the code I use for that purpose, but it does not handle
generators or iterators. You could easily combine this with code
suggested in other replies to accomplish that. (I intentionally excluded
iterators because I didn't need them and they can be unbounded. Still,
now that they are so widely used it may be worth reconsidering.)

def isSequence(item):
"""Return True if the input is a non-string sequential collection,
False otherwise. Note: dicts and sets are not sequential.
"""
try:
item[0:0]
except (AttributeError, TypeError):
return False
return not isString(item)

def isString(item):
"""Return True if the input is a string-like sequence.
Strings include str, unicode and UserString objects.

From Python Cookbook, 2nd ed.
"""
return isinstance(item, (basestring, UserString.UserString))

def asSequence(item):
"""Converts one or more items to a sequence,
If item is already a non-string-like sequence, returns it unchanged,
else returns [item].
"""
if isSequence(item):
return item
else:
return [item]

I then use asSequence on each argument that can either be one item or a
sequence of items (so I can always work on a sequence).

-- Russell

P.S. this code is from RO.SeqUtil
<http://www.astro.washington.edu/rowen/ROPython.html>
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,276
Messages
2,571,384
Members
48,073
Latest member
ImogenePal

Latest Threads

Top