Flatten iterator for nested list in python3 flatten.py

from collections import Iterable, Mapping
from operator import methodcaller

from stack import Stack


class flatten(object):
    """
    Iterator that walk through nested iterable.
    If the initial item is not iterable, only itself will be yielded.
    Args:
        item         (Object): Any object.
        depth (Optional[int]): Maximum nesting layer.
        mapping_iter    (str): the method to be called when handling mapping

    &gt;&gt;&gt; from collections import OrderedDict
    &gt;&gt;&gt; kwargs = OrderedDict()
    &gt;&gt;&gt; kwargs['file'] = 'test.txt'
    &gt;&gt;&gt; kwargs['data'] = [[0, 1], [-1, 0]]
    &gt;&gt;&gt; tuple(flatten(kwargs))
    ('test.txt', 0, 1, -1, 0)
    &gt;&gt;&gt; tuple(flatten('apple'))
    ('apple',)
    """

    def __init__(self, item, depth=128, map_iter='values'):
        self.map_iter = methodcaller(map_iter)

        done, item = self.expand(item)
        if done:
            item = iter((item,))
        self.stack = Stack([item], depth)

    def __iter__(self):
        return self

    def __next__(self):
        while self.stack:
            for item in self.stack[-1]:
                done, item = self.expand(item)
                if done:
                    return item
                else:
                    self.stack.append(item)
                    break
            else:
                self.stack.pop()
        raise StopIteration

    def expand(self, item):
        if isinstance(item, str):
            return True, item
        elif isinstance(item, Mapping):
            return False, iter(self.map_iter(item))
        elif isinstance(item, Iterable):
            return False, iter(item)
        else:
            return True, item


class named_flatten(flatten):
    """
    Iterator that walk through nested iterable and create tuple key.
    For non Mapping iterable, the key are generated by enumerate.
    If the initial item is not iterable, only itself will be yielded.
    Args:
        item         (Object): Any object.
        depth (Optional[int]): Maximum nesting layer.

    &gt;&gt;&gt; from collections import OrderedDict
    &gt;&gt;&gt; kwargs = OrderedDict()
    &gt;&gt;&gt; kwargs['file'] = 'test.txt'
    &gt;&gt;&gt; kwargs['data'] = [[0, 1], [-1, 0]]
    &gt;&gt;&gt; result = tuple(named_flatten(kwargs))
    &gt;&gt;&gt; result[0]
    (('file',), 'test.txt')
    &gt;&gt;&gt; result[1]
    (('data', 0, 0), 0)
    &gt;&gt;&gt; tuple(named_flatten('apple'))
    (((), 'apple'),)
    """

    def __init__(self, item, depth=128):
        super().__init__(((), item), depth, map_iter)

    def expand(self, item):
        key, value = item
        if isinstance(value, str):
            return True, item
        elif isinstance(value, Mapping):
            it = value.items()
        elif isinstance(value, Iterable):
            it = enumerate(value)
        else:
            return True, item
        return False, ((key+(k,), v) for k, v in it)


if __name__ == '__main__':
    import doctest
    doctest.testmod()


stack.py

from collections import deque


class FullStackError(MemoryError):
    pass


class Stack(deque):

    @property
    def full(self):
        return 0 == (self.maxlen - len(self) if self.maxlen else self.maxlen)

    def append(self, x):
        if self.full:
            raise FullStackError
        super().append(x)

    def appendleft(self, x):
        if self.full:
            raise FullStackError
        super().appendleft(x)

    def insert(self, i, x):
        if self.full:
            raise FullStackError
        super().insert(i, x)

    def extend(self, iterable):
        for x in iterable:
            self.append(x)

    def extendleft(self, iterable):
        for x in iterable:
            self.appendleft(x)

    def __str__(self):
        return 'Stack' + super().__str__()[5:]

    def __repr__(self):
        return 'Stack' + super().__repr__()[5:]
Conceptually, this is a recursion problem.  You've unrolled the recursion into iteration to support the __iter__() method.  However, this is precisely the kind of problem that generators can solve trivially!  (Here, I've used yield from, which is a feature introduced in Python 3.3.)

from collections import Iterable, Mapping
from operator import methodcaller

def flatten(it, map_iter='values', max_depth=128):
    if max_depth &lt; 0:
        raise RecursionError('maximum recursion depth exceded in flatten')
    elif isinstance(it, str):
        yield it
    elif isinstance(it, Mapping):
        for item in methodcaller(map_iter)(it):
            yield from flatten(item, map_iter=map_iter, max_depth=max_depth-1)
    elif isinstance(it, Iterable):
        for item in it:
            yield from flatten(item, map_iter=map_iter, max_depth=max_depth-1)
    else:                                              
        yield it


I've named the parameter it, which is a triple entendre that can be interpreted as "iterable", "item", or just the third-person singular impersonal pronoun.

Instead of FullStackError, which leaks information about how your iterator is implemented, just use the built-in RecursionError.  Personally, I'd choose to scrap the depth limit altogether, since the Python interpreter's stack will have its own natural recursion limit.