Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to tell a variable is iterable but not a string

Tags:

python

I have a function that take an argument which can be either a single item or a double item:

def iterable(arg)     if #arg is an iterable:         print "yes"     else:         print "no" 

so that:

 >>> iterable( ("f","f") ) yes  >>> iterable( ["f","f"] ) yes  >>> iterable("ff") no 

The problem is that string is technically iterable, so I can't just catch the ValueError when trying arg[1]. I don't want to use isinstance(), because that's not good practice (or so I'm told).

like image 253
priestc Avatar asked Jun 28 '09 17:06

priestc


People also ask

How do you know if a variable is iterable?

As of Python 3.4, the most accurate way to check whether an object x is iterable is to call iter(x) and handle a TypeError exception if it isn't. This is more accurate than using isinstance(x, abc. Iterable) , because iter(x) also considers the legacy __getitem__ method, while the Iterable ABC does not.

Is string not iterable?

Strings are iterable; iteration over a string yields each of its 1-byte substrings in order. But String doesn't implement Iterable 's Iterate method.

Is a string an iterable?

String is iterable Arrays and strings are most widely used built-in iterables.

How can you tell if an object is an iterator?

An object is Iterable if it can give you Iterator . It does so when you use iter() on it. An object is Iterator if you can use next() to sequentially browse through its elements. For example, map() returns Iterator and list is Iterable .


2 Answers

Use isinstance (I don't see why it's bad practice)

import types if not isinstance(arg, types.StringTypes): 

Note the use of StringTypes. It ensures that we don't forget about some obscure type of string.

On the upside, this also works for derived string classes.

class MyString(str):     pass  isinstance(MyString("  "), types.StringTypes) # true 

Also, you might want to have a look at this previous question.

Cheers.


NB: behavior changed in Python 3 as StringTypes and basestring are no longer defined. Depending on your needs, you can replace them in isinstance by str, or a subset tuple of (str, bytes, unicode), e.g. for Cython users. As @Theron Luhn mentionned, you can also use six.

like image 186
scvalex Avatar answered Sep 30 '22 18:09

scvalex


As of 2017, here is a portable solution that works with all versions of Python:

#!/usr/bin/env python import collections import six   def iterable(arg):     return (         isinstance(arg, collections.Iterable)          and not isinstance(arg, six.string_types)     )   # non-string iterables     assert iterable(("f", "f"))    # tuple assert iterable(["f", "f"])    # list assert iterable(iter("ff"))    # iterator assert iterable(range(44))     # generator assert iterable(b"ff")         # bytes (Python 2 calls this a string)  # strings or non-iterables assert not iterable(u"ff")     # string assert not iterable(44)        # integer assert not iterable(iterable)  # function 
like image 38
sorin Avatar answered Sep 30 '22 18:09

sorin