Объединение Python в итераторы - PullRequest
0 голосов
/ 25 апреля 2018

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

т.е.

base_path = ["/a", "/b"], subdir_lists = [ ["1", "2"], ["c", "d"] ] 

, тогда выходные данные должны быть

[ "/a", "/a/1", "/a/1/c", "/a/1/d", "a/2", "/a/2/c", "/a/2/d", "/b", "/b/1", ... "/b/2/d" ]

Мой код на Python выглядит примерно так.Я вызываю appendpaths () рекурсивно.

def appendpaths(subdir_lists, base_path):
        if not subdir_lists or len(subdir_lists) == 0:
                return base_path
        if len(subdir_lists) == 1:
                return starmap(os.path.join, product(base_path, subdir_lists[0]))
        right = subdir_lists[1:]
        iter_list = [base_path, appendpaths(right, starmap(os.path.join, product(base_path, subdir_lists[0])))]
        return chain(*iter_list)


def main():
        subdir_lists = [["1", "2"], ["c", "d"]]
        it = appendpaths(subdir_lists, ["/a", "/b"])
        for x in it:
                print(x)
main()

В моем выводе отсутствует несколько перестановок:

/a
/b
/a/1/c
/a/1/d
/a/2/c
/a/2/d
/b/1/c
/b/1/d
/b/2/c
/b/2/d

Вы видите, что я пропускаю / a / 1, / a/ 2, / b / 1 и / b / 2.Я предполагаю, что это потому, что где-то в моем коде я уже исчерпал генераторы, которые перебирают эти перестановки?

Ответы [ 2 ]

0 голосов
/ 19 июля 2018

С учетом

>>> import pathlib
>>> import itertools as it

>>> base = ["/a", "/b"]
>>> subdirs = [["1", "2"], ["c", "d"]] 

Рецепт вспомогательных itertools:

>>> def powerset(iterable):
...     "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
...     s = list(iterable)
...     return it.chain.from_iterable(it.combinations(s, r) for r in range(len(s)+1))

Код

>>> def subsequence(iterable, pred=None):
...     """Return a non-contiguous subsequence."""
...     if pred is None: pred = lambda x: x
...     return (x for x in powerset(iterable) if x and pred(x))


>>> prods = list(it.product(base, subdirs[0], subdirs[1]))
>>> pred = lambda x: x[0].startswith("/")
>>> result = sorted(set(it.chain.from_iterable(subsequence(p, pred) for p in prods)))
>>> result
[('/a',),
 ('/a', '1'),
 ('/a', '1', 'c'),
 ('/a', '1', 'd'),
 ('/a', '2'),
 ('/a', '2', 'c'),
 ('/a', '2', 'd'),
 ('/a', 'c'),
 ('/a', 'd'),
 ('/b',),
 ('/b', '1'),
 ('/b', '1', 'c'),
 ('/b', '1', 'd'),
 ('/b', '2'),
 ('/b', '2', 'c'),
 ('/b', '2', 'd'),
 ('/b', 'c'),
 ('/b', 'd')]

Приложения

Объединение путей в виде строк или pathlib объектов.

>>> ["/".join(x) for x in result];
['/a', '/a/1', '/a/1/c', ...]

>>> [pathlib.Path(*x) for x in result];
[WindowsPath('/a'), WindowsPath('/a/1'), WindowsPath('/a/1/c'), ...]

информация

Steps

  1. prods - это все itertools.product с , которые принимают итерации и создают уникальные комбинации (или декартовы произведения) способом, аналогичным диалоговому приложению выбора даты . См. Примеры ниже.
  2. subsequence - это просто оболочка рецепта powerset itertools . Это позволяет использовать pred icate, который используется для фильтрации результатов, начинающихся с косых черт, таких как base.
  3. result сортирует уплощенный набор подпоследовательностей, сгенерированных для каждого продукта. При желании вы можете присоединиться к каждому элементу. См. Код - Приложения.

Примеры

Вот декартовы произведения:

>>> prods
[('/a', '1', 'c'),
 ('/a', '1', 'd'),
 ('/a', '2', 'c'),
 ('/a', '2', 'd'),
 ('/b', '1', 'c'),
 ('/b', '1', 'd'),
 ('/b', '2', 'c'),
 ('/b', '2', 'd')]

Без предиката допускаются нежелательные подпоследовательности:

>>> list(subsequence(prods[0]))
[('/a',),
 ('1',),                                                 # bad
 ('c',),
 ('/a', '1'),                           
 ('/a', 'c'),
 ('1', 'c'                                               # bad
 ('/a', '1', 'c')]

Таким образом, мы фильтруем нежелательные элементы с помощью предиката, pred.

>>> list(subsequence(prods[0], pred=pred))
[('/a',), ('/a', '1'), ('/a', 'c'), ('/a', '1', 'c')]
0 голосов
/ 25 апреля 2018

Вы слишком усложняете это - если вы просто хотите, чтобы продукт с последовательным списком представлял собой простую рекурсию, объединяющую ранее объединенные пути (или базу), перемещаясь на один уровень глубже в каждой рекурсии все, что вам нужно:

import os

def append_paths(base, children):
    paths = []
    for e in base:
        paths.append(e)
        if children:  # dig deeper
            paths += append_paths([os.path.join(e, c) for c in children[0]], children[1:])
    return paths

И для проверки:

base_path = ["/a", "/b"]  # you might want to prepend with os.path.sep for cross-platform use
subdir_lists = [["1", "2"], ["c", "d"]]

print(append_paths(base_path, subdir_lists))
# ['/a', '/a/1', '/a/1/c', '/a/1/d', '/a/2', '/a/2/c', '/a/2/d',
#  '/b', '/b/1', '/b/1/c', '/b/1/d', '/b/2', '/b/2/c', '/b/2/d']
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...