2016-12-29 163 views
3

我有三个由其他函数生成的列表。假设现在他们是:嵌套列表的组合

x = ['d', 'e'] 
g = ['1', '2'] 
y = ['f', g] 

正如你所看到的,gy一部分。我正试图获得三个列表元素的所有组合。我试图要对此有两种方式:

方式一:

l = [] 
l.append([a]+[b] for a in x for b in y) 

另一种方法使用itertools:

import itertools 
l = list(itertools.product([a for a in x], [b for b in y])) 

这两种方式产生以下组合:

[('d', 'f'), ('d', ['1', '2']), ('e', 'f'), ('e', ['1', '2'])] 

但我想得到的是:

[('d', 'f'), ('d', '1'), ('d','2'), ('e', 'f'), ('e', '1'), ('e','2')] 

另外,当例如x为空时,我仍然没有得到任何组合,当我仍然期望获得其余两个列表的元素组合。

+7

你为什么不只是压扁y个第一和那么使用'itertools.product'? – BrenBarn

回答

3

这是从@Psidoms答案的启发而只是采用了特别定制flatten功能,以确保只有项目应该扁平化的迭代:

def flatten(x, types=list): 
    lst = [] 
    for item in x: 
     if isinstance(item, types): 
      for subitem in item: 
       lst.append(subitem) 
     else: 
      lst.append(item) 
    return lst 

>>> from itertools import product 

>>> list(product(x, flatten(y))) 
[('d', 'f'), ('d', '1'), ('d', '2'), ('e', 'f'), ('e', '1'), ('e', '2')] 

注意,有不幸的是在标准库中无此flatten功能,但你可以也可以使用外部库中的一个,例如iteration_utilities.deepflatten。注意,这需要提供strbasestringignore

>>> from iteration_utilities import deepflatten 

>>> list(product(x, deepflatten(y, ignore=str))) 
[('d', 'f'), ('d', '1'), ('d', '2'), ('e', 'f'), ('e', '1'), ('e', '2')] 

要排除空iterables从产品只是排除空subiterables。例如:

>>> x = [] 
>>> iterables = [subiterable for subiterable in (x, list(deepflatten(y, ignore=str))) if subiterable] 
>>> list(product(*iterables)) 
[('f',), ('1',), ('2',)] 
+1

打我吧:)我认为可能需要检查'item'是否从抽象基类'abc.Iterable'继承,然后确保它不是一个字符串,然后将这些元素拉入顶层。这将让我们从发电机,地图,元组等等中得到东西... –

+2

@PatrickHaugh是的,我认为确定类型或排除可能会更好,但我已经做了一次['iteration_utilities.deepflatten'](http:/ /iteration-utilities.readthedocs.io/en/latest/api/cfuncs.html#iteration_utilities.deepflatten)(一个纯粹的python实现可以在该函数docstring的底部找到),不需要再次重复。 :-) – MSeifert

+0

谢谢@MSeifert。这是一个很好的答案。 –

6

由于@BrenBarn评论,您可以拼合列表ychain功能,然后用product

from itertools import product, chain 

list(product(x, chain.from_iterable(y))) 
# [('d', 'f'), ('d', '1'), ('d', '2'), ('e', 'f'), ('e', '1'), ('e', '2')] 
+2

即使单个字符字符串是可迭代的,这真是太好了,否则这将不起作用!我希望我有一个(上)的投票。 – MSeifert

+1

@MSeifert对,如果元素是int/float,它将不起作用。 – Psidom

+3

请注意,如果您更改'y = ['fall',g]'或其他多字符字符串,它会显示潜在的不良行为 –

1

我想指出两种实现用于more_itertools可用平铺状的功能(通过pip install more_itertools安装)。

flattenitertools recipe和模拟@ Psidom的建议:

import itertools as it 
import more_itertools as mit 

list(it.product(x, mit.flatten(y))) 
# [('d', 'f'), ('d', '1'), ('d', '2'), ('e', 'f'), ('e', '1'), ('e', '2')] 

然而,扁平化更深层嵌套iterables,可以考虑使用collapse

# Example 
x = ['d', 'e'] 
g = [('1'), [[['2']]]] 
y = [{'f'}, g] 

# Bad 
list(it.product(x, mit.flatten(y))) 
# [('d', 'f'), ('d', '1'), ('d', [[['2']]]), ('e', 'f'), ('e', '1'), ('e', [[['2']]])] 

# Good 
list(it.product(x, mit.collapse(y))) 
# [('d', 'f'), ('d', '1'), ('d', '2'), ('e', 'f'), ('e', '1'), ('e', '2')] 
+1

“more_itertools.collapse”的好替代方案,但是这比使用我在['iteration_utilities.deepflatten']中呈现的纯Python实现要慢[http://iteration-utilities.readthedocs.io/en/latest/ api/cfuncs.html#iteration_utilities.deepflatten),比使用'iteration_utilities.deepflatten'本身慢得多。也许还有一些改进的余地! – MSeifert

+0

@ MSeifert也许还有改进的空间,但是你认为“慢得多”呢?我在'mit.collapse(y)'和'iteration_utilities.deepflatten(y)'上运行了Jupyter的'%timeit'命令,每个命令都有〜700 ns。你观察过什么?此外,我发现'崩溃'展开了深度嵌套迭代,例如'[[1],2,[[3],4],[[5]]],'abc']'没有关键字,这可能是一个合理的折衷。 – pylang

+0

都返回生成器,所以你把它们投到列表中或以其他方式迭代它们?因为我得到了15us的'collapse'和4us的'deepflatten'。但是,如果迭代次数变得更长,差异会变得更大。 – MSeifert