Обозначим одну подстановку как "терм", и допустим, что все варианты в терме уникальны (т.е. нет повторений внутри одного терма).
Для терма {} количество комбинаций равно количеству вариантов в терме.
Для терма [] количество комбинаций равно количеству перестановок вариантов в терме, т.е. факториал от количества комбинаций.
Для всего выражения количество комбинаций должно быть равно произведению количеств комбинаций для каждого терма.
С вложенными вариантами чуть сложнее.
Для {} количество вариантов это по сути сумма их весов.
Для [] количество вариантов считается так: количество перестановок вариантов, умноженное на произведение весов вариантов.
У простой строки вес 1, у вложенного терма вес равен количеству его комбинаций.
Таким образом, можно посчитать число комбинаций рекурсивно.
import math
class RandomChoice(list):
pass
class RandomOrder(list):
pass
def random_choice(options) -> int:
total = 0
for option in options:
if isinstance(option, RandomChoice): # вложенный выбор варианта
total += random_choice(option)
elif isinstance(option, RandomOrder): # вложенное переупорядочивание
total += random_order(option)
else:
total += 1
return total
def random_order(options) -> int:
total = math.factorial(len(options))
for option in options:
if isinstance(option, RandomChoice): # вложенный выбор варианта
total *= random_choice(option)
elif isinstance(option, RandomOrder): # вложенное переупорядочивание
total *= random_order(option)
# а для просто варианта ничего делать не надо
return total
def total_count(items) -> int:
total = 1
for item in items:
if isinstance(item, RandomChoice): # вложенный выбор варианта
total *= random_choice(item)
elif isinstance(item, RandomOrder): # вложенное переупорядочивание
total *= random_order(item)
return total
sample = [
'я',
RandomChoice(['купил', 'приобрел']), # 2
RandomChoice(['на ярмарке','на рынке','в магазе']), # 3
RandomChoice(['сыр', #8
RandomOrder(['соль, ','хлеб, ','муку, ']), # 6
'сахар']),
]
combos = total_count(sample)
print(combos)