Во-первых, вам не нужно писать по отдельности twofunc и threefunc:
def nfunc(strings, indices):
"""call as nfunc(main,(2,0)) or nfunc(main, (2,0,4))"""
selected_strings=map(lambda index:strings[index], indices)
return itertools.product(*selected_strings)
Обратите внимание, что это просто возвращает итеративный объект, поэтому он будет вычисляться лениво.
Теперьмы можем разделить список индексов из императивного кода как список, как вы сделали со строками, так что вам не нужно переписывать вызовы каких-либо функций для изменения индексов:
index_list=[(2, 0),(3, 0),...,(2, 0, 4),(3, 0, 4),...,(3, 0, 6)]
Toпросто получите список ленивых результатов, теперь вы можете написать:
lazy_results=[nfunc(main, indices) for indices in index_list]
-> [itertools.product at 0xblahblah, itertools.product at 0xblahblah, ...]
, чтобы форсировать оценку результатов:
eager_results=[list(lazy) for lazy in lazy_results]
-> [[("cheap","car insurance"),("budget","car insurance"), ...
, чтобы сгладить кортежи в строки:
str_results=[[' '.join(rtuple) for rtuple in result] for result in eager_results]
-> [["cheap car insurance", "budget car insurance", ...
и вы можете добавить произвольные теги, если они вам нужны:
tag_index_list=[(tag_1,(2,0)), ... (tag_n, (3, 0, 6))]
tag_results=[(tag, nfunc(main, indices)) for (tag, indices) in tag_index_list]
В зависимости от того, что вам нужно сделать, может лучше написать класс для вашего результатаустановить вместо;если вы пройдете через приведенный выше код, вы почувствуете, как будет выглядеть использование вложенных списков и кортежей.