Based on the excellent answers provided for my previous question, I've thoroughly revised my attempt at "deep" versions of
any, etc. It uses the recommended encapsulated flattening function, and streamlines some unnecessary stuff.
def flatten(*sequence, preserve=False): for item in sequence: try: assert hasattr(item, '__iter__') and not isinstance(item, str) if preserve: next(iter(item)) except (AssertionError, StopIteration): yield item else: for i in item: yield from flatten(i, preserve=preserve) def dsum(*args, preserve=False, s=''): if any(isinstance(item, str) for item in flatten(args)): return s.join(map(str, flatten(args, preserve=preserve))) iter_ = flatten(args, preserve=preserve) first = next(iter_) return sum(iter_, first) def dlen(*args, preserve=False, s=False): return sum(len(arg) if s and isinstance(arg, str) else 1 for arg in flatten(args, preserve=preserve))
flatten() function is "public" and intended to be accessed from outside the module. It handles an arbitrary number of arguments and generates each non-iterable element (strings are treated as non-iterable). If the keyword argument
preserve is set to
True, empty iterables are preserved.
I am not aware of any way to use
if to test whether a generator/iterable has any items to
yield - is there such a way? If not, is my use of
assert an acceptable way of turning the
if statements into exceptions, or is it better to use
if... raise, or something else entirely?
dsum() function adds together all the items yielded by
True, empty iterables are preserved. If at least one item is a string, everything is converted into a string and joined, connected by the optional
s argument. If there are no strings, all items after the first are added onto the first item. If there aren't any strings and there are items that can't be added, the function fails, which I find acceptable.
dlen() function finds the total length of all the items yielded by
True, empty iterables are preserved and counted as a length of 1. Strings are counted as objects of length 1 unless the optional keyword argument
True, in which case their actual lengths (number of characters) are counted.
I have removed
dall(), etc. because they were just wrappers around
flatten() with zero additional logic.
Test suite (passes):
from deep import * import datetime as dt assert dsum([1,2], 3, '1','2','3','456', ) == '123123456' assert dsum([1,2], 3, '1','2','3','456', , preserve=True) == '123123456' assert dsum('a', 'b') == 'ab' assert dsum(1, [2,[3,[4,,]]]) == 15 # dsum(1, [2,[3,[4,,]]], preserve=True) # can't sum int with preserved list assert all(flatten(1, 1, [1, ])) assert not all(flatten(1, 1, [1, ], preserve=True)) assert all(flatten(1, 1, set())) assert not all(flatten(1, 1, set(), preserve=True)) assert list(flatten((1, 1, ))) == [1,1] assert list(flatten((1, 1, ), preserve=True)) == [1,1,] assert dsum(dt.timedelta(3), dt.timedelta(4), s=dt.timedelta()) == dt.timedelta(7)