【问题标题】:Else clause in a python list comprehension with two loops带有两个循环的python列表理解中的Else子句
【发布时间】:2016-11-24 15:38:00
【问题描述】:

所以我有一个名为 dict1 的列表嵌套字典,如下所示:

{"types": [{"name": "Chinese", 
            "sub": 
                [{
                  "menu": [{"name": "Ya", "cost": 1}, {"name": "Ja", "cost": 2}], 
                  "name": "Soups"
                 }, 
                 {
                  "menu": [{"name": "Ta", "cost": 3}, {"name": "Ba", "cost": 4}], 
                  "name": "Mains"
                }]
           },
           {"name": "Indian(Mains)",
            "menu": [{"name": "Sa", "cost": 5}, {"name": "Pa", "cost": 6}]
           }
         ]
}

我最终需要的是:

{"types": [{"name": "Chinese(Soups)",
            "menu": [{"name": "Ya", "cost": 1}, {"name": "Ja", "cost": 2}]},

           {"name": "Chinese(Mains)",
            "menu": [{"name": "Ta", "cost": 3}, {"name": "Ba", "cost": 4}]},

           {"name": "Indian(Mains)",
            "menu": [{"name": "Sa", "cost": 5}, {"name": "Pa", "cost": 6}]}
          ]
}

总而言之,如果内部字典包含 sub 键,请将其分成单独的字典,否则保持原样。由于我最了解的原因,我对此使用列表推导。这是我到目前为止所拥有的:

dict1['types'] = [{'name': '%s(%s)' % (outer['name'], inner['name']),
                   'menu': inner['menu']}
                   for outer in dict1['types']
                   if 'sub' in outer 
                   for inner in outer['sub']]

这给了我以下输出:

{"types": [{"name": "Chinese(Soups)",
            "menu": [{"name": "Ya", "cost": 1}, {"name": "Ja", "cost": 2}]},

           {"name": "Chinese(Mains)",
            "menu": [{"name": "Ta", "cost": 3}, {"name": "Ba", "cost": 4}]}
          ]
}

很明显,它只给了我 2 个 dicts 而不是 3 个,这很明显,因为我没有 else 子句。我的问题是,在这种情况下,如何在具有两个循环的列表理解中使用 else 子句?

编辑 正如 Martijn 在 cmets 中提到的,这对于列表理解是不可能的,我也可以通过修改现有的 dict 来实现这一点的其他方法

【问题讨论】:

  • if 在列表理解循环中只能过滤(排除)项目。没有else 选项,因为包含一个项目是默认设置。
  • @Martijn Pieters..能否请您告诉我在不创建新字典和修改现有字典的情况下完成此任务的最佳方法是什么?

标签: python python-2.7 list dictionary list-comprehension


【解决方案1】:

在列表理解循环中,if 仅允许您过滤您迭代的项目,而不是换出更多循环。因为if 充当过滤器,所以在包含迭代元素中没有else 作为默认操作。

您必须在内部循环中使用表达式来选择 for 循环的两个备选方案之一进行迭代:

[{'name': '%s(%s)' % (outer['name'], inner['name']), 'menu': inner['menu']} if 'sub' in outer else inner
 for outer in dict1['types']
 for inner in (outer['sub'] if 'sub' in outer else [outer])]

所以这会选择outer['sub'] 列表或[outer] 列表来迭代inner 名称。然后,您可以使用条件表达式生成新字典,或者如果 outer 中不存在 'sub' 键,则只需重新使用 inner(实际上是 outer)。

当然,您可以使用dict.get() 来简化它:

[{'name': '%s(%s)' % (outer['name'], inner['name']), 'menu': inner['menu']} if 'sub' in outer else inner
 for outer in dict1['types']
 for inner in outer.get('sub', [outer])]

演示:

>>> dict1 = {"types": [{"name": "Chinese",
...             "sub":
...                 [{
...                   "menu": [{"name": "Ya", "cost": 1}, {"name": "Ja", "cost": 2}],
...                   "name": "Soups"
...                  },
...                  {
...                   "menu": [{"name": "Ta", "cost": 3}, {"name": "Ba", "cost": 4}],
...                   "name": "Mains"
...                 }]
...            },
...            {"name": "Indian(Mains)",
...             "menu": [{"name": "Sa", "cost": 5}, {"name": "Pa", "cost": 6}]
...            }
...          ]
... }
>>> [{'name': '%s(%s)' % (outer['name'], inner['name']), 'menu': inner['menu']} if 'sub' in outer else inner
...  for outer in dict1['types']
...  for inner in outer.get('sub', [outer])]
[{'name': 'Chinese(Soups)', 'menu': [{'name': 'Ya', 'cost': 1}, {'name': 'Ja', 'cost': 2}]}, {'name': 'Chinese(Mains)', 'menu': [{'name': 'Ta', 'cost': 3}, {'name': 'Ba', 'cost': 4}]}, {'name': 'Indian(Mains)', 'menu': [{'name': 'Sa', 'cost': 5}, {'name': 'Pa', 'cost': 6}]}]
>>> from pprint import pprint
>>> pprint(_)
[{'menu': [{'cost': 1, 'name': 'Ya'}, {'cost': 2, 'name': 'Ja'}],
  'name': 'Chinese(Soups)'},
 {'menu': [{'cost': 3, 'name': 'Ta'}, {'cost': 4, 'name': 'Ba'}],
  'name': 'Chinese(Mains)'},
 {'menu': [{'cost': 5, 'name': 'Sa'}, {'cost': 6, 'name': 'Pa'}],
  'name': 'Indian(Mains)'}]

就个人而言,我会在这里使用生成器函数,而不是单个列表推导:

def flattened_menu(types):
    for entry in types:
        if 'sub' in entry:
            # flatten out nested menu entries
            for subentry in entry['sub']:
                yield {
                    'name': '{0[name]}({1[name]})'.format(entry, subentry),
                    'menu': subentry['menu']}
        else:
            yield entry

dict1['types'] = list(flattened_menu(dict1['types']))

【讨论】:

  • 只是为了理解..有没有更好的方法而不使用列表理解?
  • @Amistad:我添加了一个替代方案;不要害怕在这里使用更冗长的函数,它可以让以后更容易弄清楚代码的作用。
猜你喜欢
  • 2016-10-05
  • 1970-01-01
  • 1970-01-01
  • 2018-09-14
  • 2021-12-23
  • 2022-12-10
  • 1970-01-01
  • 1970-01-01
  • 2021-10-19
相关资源
最近更新 更多