2016-12-29 108 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