Очистить динамически генерируемые вложенные кортежи

Я реализовал алгоритм, чтобы сделать декартово произведение произвольно вложенных диктов в Python. Поля, находящиеся на корневом уровне, необходимо реплицировать в несколько строк, поскольку в него могут быть также вложены массивы. Я использовал itertools.product рекурсивно, агрегируя промежуточные итераторы.

Работает, но проблема в распаковке на финале. Я получил такую ​​структуру:

(11, 12.2, 123.2, 1.23, 104.75, (10.7, 104.75, 104.75, ('N', True, False, 'B2B'), 99.01, 6.3, 1.23, 5.87, 12.2), 1, 'SP', 7),

Я ищу понимание списка, выражение генератора или даже полноценный генератор, который может сгладить и очистить этот ряд, чтобы:

(11, 12.2, 123.2, 1.23, 104.75, 10.7, 104.75, 104.75, 'N', True, False, 'B2B', 99.01, 6.3, 1.23, 5.87, 12.2, 1, 'SP', 7),

Какой самый лучший и быстрый способ сделать это?

РЕДАКТИРОВАТЬ

На самом деле, я попросил понять список или выражение генератора или даже полноценный генератор, потому что мне нужно включить его в ловушку, перехватывая выходные данные itertools.product сам. Мне просто не нужен способ почистить эти чаши. Так что это не дубликат.

Ребята, пожалуйста, я не хочу никакой рекурсивной функции для этого! У меня есть класс, чей __iter__() метод вернуть itertools.product динамически генерируемых данных. Я пытаюсь любой из этих:

  1. Измените этот метод для обработки внутренних кортежей:

    class Explosion:
        ...
        def __iter__(self):
            return product(*self.fragments)
    
  2. Инкапсулируйте в другой объект, обрабатывающий преобразование, но это менее желательно:

    class CleanOutput:
        def __init__(self, it):
            self.it = it
    
        def next(self):
            for x in self.it:
                yield ?
    
    class Explosion:
        ...
        def __iter__(self):
            return CleanOutput(product(*self.fragments))
    

2 ответа

def gen(data):
   for item in data:
      if isinstance(item, tuple):
          for nested in gen(item):
              yield nested
      else:
          yield item

Не проверено, но должно работать.

Это было непросто, рекурсия должна быть использована, но отделена от основной __iter__ метод. Вот так я и сделал. Теперь также с рекурсивным генератором _mergeвызывается другим генератором _flatten:

class Explosion:
    # ...

    def __iter__(self):
        def _flatten(container):
            def _merge(t):
                for te in t:
                    if isinstance(te, tuple):
                        for ite in _merge(te):
                            yield ite
                    else:
                        yield te

            for t in container:
                yield tuple(_merge(t))

        return _flatten(product(*self.fragments))

Смотрите пример использования _flatten() функция:

>>> list(itertools.product([1,2],[3,(4,(5,6))]))
[(1, 3), (1, (4, (5, 6))), (2, 3), (2, (4, (5, 6)))]
>>> list(_flatten(itertools.product([1,2],[3,(4,(5,6))])))
[(1, 3), (1, 4, 5, 6), (2, 3), (2, 4, 5, 6)]
Другие вопросы по тегам