У вас произведение строки трижды на себя. Строка длиной 3. Итого 3**3 == 27 вариантов.
Каждому элементу можно поставить в соответствие порядковый номер.
for i in range(27):
print(f'{i:2}:', i // 9 % 3, i // 3 % 3, i % 3)
Числа после двоеточий - это индексы в исходной строке.
Вы можете теперь получить (вычислить) любой i-ый элемент вашей последовательности:
s = 'abc'
def g(i):
return s[i // 9 % 3], s[i // 3 % 3], s[i % 3]
for i in range(len(s) ** 3):
print(g(i))
Можно обобщить:
def g(s, i):
n = len(s)
return [
s[i // n**(n-j-1) % n]
for j in range(n)
]
my_custom_string = 'abc'
for i in range(len(my_custom_string) ** len(my_custom_string)):
print(g(my_custom_string, i))
Ну и раз уж пошла такая пьянка, можно сделать ваше умножение индексируемым:
import itertools
import functools
import operator
class Producti:
def __init__(self, *iters, repeat=1):
iters = [list(it) for it in iters]
self.iters = iters * repeat
self._len = functools.reduce(operator.mul, map(len, self.iters))
def __len__(self):
return self._len
#def __iter__(self):
# return (self[i] for i in range(len(self)))
def __getitem__(self, idx):
if isinstance(idx, slice):
return (self[i] for i in range(len(self))[idx])
if idx >= len(self):
raise IndexError(f'product index out of range')
r = []
d = 1
for it in self.iters:
r.append(it[idx // d % len(it)])
d *= len(it)
return tuple(r[::-1])
for x in Producti('abc', repeat=3)[3:7]:
print(x)
Единственное, чем нам приходится жертвовать по сравнению со штатным product'ом - это распаковкой элементных итераторов. Нам это нужно, чтобы заранее знать как приводить число к индексам.
P.S.
Кстати, метод "__iter__", в принципе, не нужен, iter(Producti('aa', '12')) проитерируется теперь и так (когда есть исключение по выходу за пределы len).