【问题标题】:Python, flatten an ugly nested for loopPython,展平丑陋的嵌套 for 循环
【发布时间】:2019-12-19 09:40:39
【问题描述】:

我想通过两个模块来处理多个数据文件,使用每个模块的多个参数的多个设置的每种组合。最明显的方法是使用嵌套的 for 循环,但是当你达到 7+ 个嵌套的 for 循环时,不会。我想让这个比那个更优雅。

我已经阅读了几个非常相似的问题,但是虽然this one 表明我可能想要使用 itertools,但它只遍历数字序列,而我想遍历作为值包含在字典中的字符串列表; this other one 揭示了我想要的被称为笛卡尔积,但不是如何从字典值中得到它;虽然this one 结合了笛卡尔积中的列表字典,但我希望输出是上一个链接问题中的列表列表,而不是字典列表。

在:

video = ["It's Friday.mp4",'Hot Koolaid.mov','The Water Buffalo Song.mp4']
CC = {'size':['6','10','14'],'font':['Courier New'],'color':['black','white'],'language':['English']}
Noise = {'CRT':['speckles','rising stripes','no signal'],'sound':['white','crackle']}

输出:

[['It's Friday.mp4','6','Courier New','black','English','speckles','white'], 
 ['Hot Koolaid.mov','6','Courier New','black','English','speckles','white']
 ...
 ['The Water Buffalo Song.mp4','14','Courier New','white','English','no signal','crackle']]

我很确定我想使用itertools,并且我想要做的是列表的笛卡尔积。我认为目前最困难的事情是从字典中提取这些列表并将它们的元素组合放入列表中。

_________编辑:____________

在检查我随后接受的答案的过程中,我发现所有参数都在列表中很重要(出于我的目的),即使只考虑一个值;不带方括号的字符串将一次迭代一个字符。

丑陋的嵌套 for 循环看起来像:

for vid in video:
    for siz in CC['size']:
        for fon in CC['font']:
            for col in CC['color']:
                for lan in CC['language']:
                    for crt in Noise['CRT']:
                        for sou in Noise['sound']:
                            some_function(vid,siz,fon,col,lan,crt,sou)

【问题讨论】:

  • 通过您的示例输入,您怎么知道 ['black', 'white'] 意味着您有两个“黑色”项目和一个“白色”?
  • @CMMCD 不是两黑一白的意思;省略号代表了很多很多列表。笛卡尔积也可以称为阶乘;我们正在列出所有可能的开放选项组合。

标签: python list dictionary itertools cartesian-product


【解决方案1】:

字典足够小,最简单的方法是将七个参数硬编码到itertools.product:一个“独立”列表,四个来自CC 的列表,以及两个来自Noise 的列表。

from itertools import product
result = list(product(
                  video,
                  CC['size'],
                  CC['font'],
                  CC['color'],
                  CC['language'],
                  Noise['CRT'],
                  Noise['sound']
         ))

您可以使用operator.itemgetter 稍微简化一下,这样可以避免重复提及这两个字典。

from operator import itemgetter
result = list(product(
                  video,
                  *itemgetter('size', 'font', 'color', 'language')(CC),
                  *itemgetter('CRT', 'sound')(Noise)
         ))

如果您确定字典值的生成顺序,您可以进一步缩短它:

result = list(product(video, *CC.values(), *Noise.values()))

【讨论】:

  • 最后一个选择是爱python的一个原因
  • @chepner 你说使用第三个,最简洁的选项取决于确定字典会给我它们的值的顺序。我过去读过,没有确定的顺序,但是每当我打印字典(或字典的一部分)时,我总是/几乎总是按照我放入它们的顺序看到它的元素。你知道这是什么条件吗?
  • 在 Python 3.6 之前,迭代顺序是任意的。在 Python 3.6 中,顺序由将键添加到 dict 的顺序确定,但仅作为 CPython 中的实现细节。该实现细节在 Python 3.7 中成为语言要求。
  • 所以,基本上,因为我使用的是 Python 3.7,所以我可以选择优雅的形式?
  • 假设您知道添加键的顺序并且该顺序是您想要的,是的。
【解决方案2】:

如果您可以对您的数据进行一些轻微的调整以使每个键都具有单个值(例如 {'language': 'English'}),或者具有与每个视频对应的值列表(例如 {'color':['black','black','white']}),您可以还可以使用pandas 制作一个漂亮的数据表。一个例子是这样的:

video = ["It's Friday.mp4",'Hot Koolaid.mov','The Water Buffalo Song.mp4']
CC = {'size':['6','10','14'],'font':'Courier New','color':['black','black','white'],'language':'English'}
Noise = {'CRT':['speckles','rising stripes','no signal'],'sound':['white','white','crackle']}

video_df = pd.DataFrame()
video_df['video'] = video
for key in CC.keys():
    video_df[key] = CC[key]
for key in Noise.keys():
    video_df[key] = Noise[key]

video_df.values.tolist()

这将产生一个列表,如下所示:

[["It's Friday.mp4",
  '6',
  'Courier New',
  'black',
  'English',
  'speckles',
  'white'],
 ['Hot Koolaid.mov',
  '10',
  'Courier New',
  'black',
  'English',
  'rising stripes',
  'white'],
 ['The Water Buffalo Song.mp4',
  '14',
  'Courier New',
  'white',
  'English',
  'no signal',
  'crackle']]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-02-06
    • 2018-02-12
    • 2012-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-05
    相关资源
    最近更新 更多