@Energy2

Как получить все комбинации элементов строки в срезе?

Код
import itertools
for i in itertools.product('ABC', repeat=3):
	print(''.join(i))

Возвращает
AAA
AAB
AAC
ABA
ABB
ABC
ACA
ACB
ACC
BAA
BAB
BAC
BBA
BBB
BBC
BCA
BCB
BCC
CAA
CAB
CAC
CBA
CBB
CBC
CCA
CCB
CCC


Есть ли способы, скажем, указать с какого по какой элементы получить, например, указав "0, 4" возвращало
AAB
AAC
ABA
ABB

А затем, указав 7-13 в ответе было
ACC
BAA
BAB
BAC
BBA
BBB

Ну или просто, чтобы можно было получать элементы по порядку, но группами, скажем сначала 0-4, затем 4-10, потом 10-13 и тп. до последнего элемента.
Важно, чтобы предварительно полный список сгенерированных элементов не заносился в переменную, как изначально возвращает itertools.product(), а только те, с какого по какой должны сгенерироваться.
  • Вопрос задан
  • 96 просмотров
Решения вопроса 1
trapwalker
@trapwalker Куратор тега Python
Программист, энтузиаст
У вас произведение строки трижды на себя. Строка длиной 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).
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@bacon
1. возможно устроит itertools.islice
2. если нет, написать свою функцию на yield'ах
3. надо учитывать, что итераторы это только про возрастание, т.е без промежуточного сохранения, прокати только 0-4 и 5-10, но не 0-4 и 4-10, и тем более не 0-4 и 3-10
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы