Based on the discussions in this thread (thanks all for your thoughts), I'm settling for:
def is_iterable(obj): try: iter(obj).next() return True except TypeError: return False except KeyError: return False The call to iter will fail for objects that don't support the iterator protocol, and the call to next will fail for a (hopefully large) subset of the objects that don't support the sequence protocol. This seems preferable to cluttering code with exception handling and inspecting tracebacks. But it's still basically wrong, I guess. To repost my use case: def deeply_mapped(func, iterable): """ Recursively apply a function to every item in a iterable object, recursively descending into items that are iterable. The result is an iterator over the mapped values. Similar to the builtin map function, func may be None, causing the items to returned unchanged. >>> import functools >>> flattened = functools.partial(deeply_mapped, None) >>> list(flattened([[1], [2, 3, []], 4])) [1, 2, 3, 4] >>> list(flattened(((1), (2, 3, ()), 4))) [1, 2, 3, 4] >>> list(flattened([[[[]]], 1, 2, 3, 4])) [1, 2, 3, 4] >>> list(flattened([1, [[[2, 3]], 4]])) [1, 2, 3, 4] """ for item in iterable: if is_iterable(item): for it in deeply_mapped(func, item): if func is None: yield it else: yield func(it) else: if func is None: yield item else: yield func(item) -- Neil Cerutti -- http://mail.python.org/mailman/listinfo/python-list