Flatten nested iterables
Looks like decent code. I think you only got downvoted (once) because you alluded to how you wanted to use this function as part of a (gasp!) program — and then of course twice more because they saw you'd already been downvoted once. That's just how this site works. :)



The biggest problem with your code is that it uses the identifiers iterable, iterable_attr, iterator_attr, iterators, it, and iter, all with different meanings. That's too many its!

iterable_attr and iterator_attr could be called more like ITER_ATTR and NEXT_ATTR, because (A) the capitalization indicates that they're constants, and (B) the names indicate the values they hold. However, this is right up there with THREE = 3. When you see yourself giving symbolic names to global constants, it's time to DRY up your code:

    if hasattr(it, '__iter__'):
        string = isinstance(it, str)
        if not ((string and len(it) &lt;= 1) or
                (string and not strings)):
            it = iter(it)
    if hasattr(it, '__next__'):
        iterators.append(it)
        iterators = iterators[:depth]




Actually, that logical expression buried in the middle there looks like it could stand some De Morgan's Laws. Also, string is a bad name for a boolean! Name your booleans after predicates: in this case, is_string would do. But let's see if we need it at all.

        is_string = isinstance(it, str)
        if not ((is_string and len(it) &lt;= 1) or (is_string and not strings)):
            it = iter(it)


De Morgan 1:

        is_string = isinstance(it, str)
        if not(is_string and len(it) &lt;= 1) and not(is_string and not strings):
            it = iter(it)


De Morgan 2:

        isnt_string = not isinstance(it, str)
        if (isnt_string or len(it) &gt; 1) and (isnt_string or strings):
            it = iter(it)


Distributive law:

        isnt_string = not isinstance(it, str)
        if isnt_string or (strings and len(it) &gt; 1):
            it = iter(it)


Refactor to emphasize the one special case we're trying to express:

        if (not hasattr(it, '__iter__')) or (len(it) &lt;= 1):
            pass  # can't explode this guy
        elif (not strings) and isinstance(it, str):
            pass  # we're not exploding strings
        else:
            it = iter(it)  # explode it




The next two lines

        iterators.append(it)
        iterators = iterators[:depth]


look suspicious. We're appending to the array and then immediately truncating it? Couldn't we do that in some cleaner way?



By the way, that *, in the signature must be a Python-3-ism; I'm not sure what it means. In Python 2 you'd just omit it and it would still do what you wanted.



Your exhausted = object() ... if it is exhausted idiom is neat, if maybe a bit too cute. It beats catching the exception from next(iterators[-1]), anyway.



Unit tests (especially for depth=...) would go a long way here.