I keep forgetting that the standard join()
can only take a single iterable, so I made a few functions that act recursively on any passed arguments. Somewhat ironically, the deep version of join()
isn't called by the user, but only within the deep version of sum()
- the standard sum()
breaks and tells you to use join()
if you try to sum()
strings, but this one just calls the deep join()
automatically.
Several questions prompted me to post this here:
- Is this a good idea? Why didn't Google turn up any results for such a thing (i.e., why doesn't anyone seem to have tried it)?
- Could anything particularly bad happen with these, like system instability or silent failures?
- And, of course, how could they be improved? I'd like equal parts readability and speed.
It's 100 lines total, including comments, but each function is between 4 and 12 lines (plus the definition). Feel free to comment on all or any of them.
def _djoin(*args, s=''):
"""
Executes a recursive string join on all passed arguments and their contents.
Parameters:
*args (tuple): An unrolled tuple of arguments.
s (string): Optional. Separates each element with the given string.
"""
if len(args) == 1:
try:
iter(args[0])
if type(args[0]) == str:
raise TypeError
return s.join(_djoin(arg, s=s) for arg in args[0])
except TypeError:
return str(args[0])
return s.join(_djoin(arg, s=s) for arg in args)
def dall(*args):
"""
Executes a recursive all() on all passed arguments and their contents.
Parameter:
*args (tuple): An unrolled tuple of arguments.
"""
if len(args) == 1:
try:
iter(args[0])
if type(args[0]) == str or not len(args[0]):
raise TypeError
return all(dall(arg) for arg in args[0])
except TypeError:
return bool(args[0])
return all(dall(arg) for arg in args)
def dany(*args):
"""
Executes a recursive any() on all passed arguments and their contents.
Parameter:
*args (tuple): An unrolled tuple of arguments.
"""
if len(args) == 1:
try:
iter(args[0])
if type(args[0]) == str or not len(args[0]):
raise TypeError
return any(dany(arg) for arg in args[0])
except TypeError:
return bool(args[0])
return any(dany(arg) for arg in args)
def dsum(*args, s=0):
"""
Executes a recursive sum() on all passed arguments and their contents.
If s is a string, _djoin(args, s) is returned.
Parameters:
*args (tuple): An unrolled tuple of arguments.
s: An initial value to which all other values will be added.
"""
if type(s) == str:
return _djoin(*args, s=s)
if len(args) == 1:
try:
iter(args[0])
if type(args[0]) == str:
raise TypeError
return sum((dsum(arg, s=s) if arg else s for arg in args[0]), s)
except TypeError:
if type(s) == list:
return [args[0]]
return (args[0])
return sum((dsum(arg, s=s) for arg in args), s)
def ssum(*seq):
"""
Executes a sum() on the given seq, automatically determining a reasonable start value.
Parameter:
*seq (tuple): An unrolled tuple of arguments.
"""
n = next(iter(seq))
if len(seq) == 1:
return sum(n, type(next(iter(n)))())
return sum(seq, type(n)())
def dlen(*args, deep=False):
"""
Executes a recursive len() on all passed arguments and their contents.
Parameters:
*args (tuple): An unrolled tuple of arguments.
deep (bool): An initial value to which all other values will be added (with type conversions if necessary).
"""
if len(args) == 1:
try:
iter(args[0])
if type(args[0]) == str:
raise TypeError
return sum((dlen(arg, deep=deep) for arg in args[0]))
except TypeError:
if deep and type(args[0]) == str:
return len(args[0])
return 1
return sum((dlen(arg, deep=deep) for arg in args))
This is what I used to test them. Each line prints function results and intended results, successfully thus far. New, more strenuous tests to expose bugs would be welcome.
from deep import *
import datetime as dt
print(_djoin('foo', 'bar', 123, s=' '), '#### foo bar 123')
print(_djoin(['foo', 'bar', 123], s=' '), '#### foo bar 123')
print(_djoin('foo', 'bar', [123,456,789,'baz'], s=' '), '#### foo bar 123 456 789 baz')
print(_djoin(['foo', 'bar', [123,456,789,'baz']], s=' '), '#### foo bar 123 456 789 baz')
print(_djoin([10,11,12, 0.0000000000003], s=' '), '#### 10 11 12 3e-13')
print(' '.join(_djoin([10,11,12, 0.0000000000003])), '#### 1 0 1 1 1 2 3 e - 1 3')
print(dall([1],[1],[[],[]]), False)
print(dall([0],), False)
print(dall(0), False)
print(dall(1), True)
print(dall(1,2,[3]), True)
print(dall([1],), True, '\n')
print(dany([],[0],[[],[]]), False)
print(dany([0],), False)
print(dany(0), False)
print(dany(1), True)
print(dany([0,0],[3]), True)
print(dany([],[1]), True, '\n')
print(dsum(1,2,3), 6)
print(dsum([1,2,3]), 6)
print(dsum(1,[2,3]), 6)
print(dsum([1,2],[3,4],5), 15)
print(dsum([1,2],[3,4], s=[]), [1,2,3,4])
print(dsum([1,2],[3,4],5, s=[]), [1,2,3,4,5])
print(dsum(1,2,3,[4,[5,6]]), 21)
print(dsum('a','b',s='-'), 'a-b')
print(dsum(1,2,3, s='-'), '1-2-3')
print(dsum(1,2,3), 6)
print(dsum(dt.timedelta(3), dt.timedelta(4), s=dt.timedelta()), "7 days, 0:00:00")
print(dsum(1,2,3,[[],[3,0]]), 9, '\n')
print(dlen([1,2,3]), 3)
print(dlen([1,2],[3]), 3)
print(dlen([[1,2],[0],[2,[2,[2]]]]), 6)
print(dlen([['hello',2],[0],[2,[2,[2]]]]), 6)
print(dlen([['hello',2],[0],[2,[2,[2]]]], deep=True), 10, '\n')
print(ssum([[1,2],[3]]), [1,2,3])
print(ssum([1,2,3]), 6)
print(ssum(1,2,3), 6)
print(ssum([1,2],[3]), [1,2,3])