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

Jean-Paul Calderone exarkun at divmod.com
Tue Jan 10 18:56:26 EST 2006


On 10 Jan 2006 15:18:22 -0800, sandravandale at yahoo.com wrote:
>I want to be able to pass a sequence (tuple, or list) of objects to a
>function, or only one.

Generally it's better to keep your API consistent.  If you are going 
to take sequences, only take sequences.  Don't try to make the 
interface more convenient by allowing single values to be passed in 
by themselves: it just leads to confusion and complexity.

>
>It's easy enough to do:
>
>isinstance(var, (tuple, list))

Above you said sequences - what about strings (buffers, unicode and 
otherwise) or arrays or xrange objects?  Those are all sequences too, 
you know.

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

Ah, but generators *aren't* sequences.  I'm guessing you really want 
to accept any "iterable", rather than any sequence.  The difference is 
that all you can do with an iterable is loop over it.  Sequences are 
iterable, but you can also index them.

I hope you heed my advice above, 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:

   >>> iter("hello world")
   <iterator object at 0xb7cae5cc>
   >>> iter([1, 2, 3, 4])
   <listiterator object at 0xb7cae72c>
   >>> def foo():
   ...   yield 1
   ...   yield 2
   ...   yield 3
   ... 
   >>> iter(foo())
   <generator object at 0xb7cae5cc>

This works for anything that is iterable, as you can see.  However, it 
won't work for anything:

   >>> iter(5)
   Traceback (most recent call last):
     File "<stdin>", line 1, in ?
   TypeError: iteration over non-sequence

This is "duck typing" in action.

Hope this helps,

Jean-Paul



More information about the Python-list mailing list