I'll bet itertools
-based solutions are going to be faster, but if they need to be avoided (e.g. stuck on Python 2.5 without itertools.product, etc), it can of course be entirely coded in "base Python" when one must.
Trying to "pull out all the stops" for speed, maybe something like:
def odo(*names_and_valuelists):
aux = [[vl, 0] for n, vl in names_and_valuelists]
if any(len(vl)==0 for vl, _ in aux):
return
while True:
yield tuple(vl[i] for vl, i in aux)
for vlandi in reversed(aux):
if vlandi[1] == len(vlandi[0])-1:
vlandi[1] = 0
else:
vlandi[1] += 1
break
else:
return
though minor tweaks might still accelerate it (needs thorough profiling with realistic sample data!).
Here's your example of use:
def main():
data = [
('Col1', 'value11 value12 value13'.split()),
('Col2', 'value21 value22'.split()),
('Col3', 'value31 value32 value33'.split()),
]
for tup in odo(data[0], data[1]): print tup
print
for tup in odo(data[1], data[2]): print tup
print
for i, tup in enumerate(odo(*data)):
print tup
if i>5: break
if __name__ == '__main__':
main()
which emits the results:
('value11', 'value21')
('value11', 'value22')
('value12', 'value21')
('value12', 'value22')
('value13', 'value21')
('value13', 'value22')
('value21', 'value31')
('value21', 'value32')
('value21', 'value33')
('value22', 'value31')
('value22', 'value32')
('value22', 'value33')
('value11', 'value21', 'value31')
('value11', 'value21', 'value32')
('value11', 'value21', 'value33')
('value11', 'value22', 'value31')
('value11', 'value22', 'value32')
('value11', 'value22', 'value33')
('value12', 'value21', 'value31')