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
>>> from collections import OrderedDict
>>> kwargs = OrderedDict()
>>> kwargs['file'] = 'test.txt'
>>> kwargs['data'] = [[0, 1], [-1, 0]]
>>> tuple(flatten(kwargs))
('test.txt', 0, 1, -1, 0)
>>> 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.
>>> from collections import OrderedDict
>>> kwargs = OrderedDict()
>>> kwargs['file'] = 'test.txt'
>>> kwargs['data'] = [[0, 1], [-1, 0]]
>>> result = tuple(named_flatten(kwargs))
>>> result[0]
(('file',), 'test.txt')
>>> result[1]
(('data', 0, 0), 0)
>>> 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:]