将列表的定义从迭代转换为理解

时间:2019-01-31 21:07:58

标签: python python-3.x dictionary-comprehension

我有一个方法,该方法采用边列表,边具有以下形式:(v1,v2,capacity)并返回以下形式的字典:

dico = {v1:{v2:capacity,v3:capacity} v2:...}

dico代表图形

我想通过理解的方式来定义这个dico,但是我真的很受阻,我什至不知道我们能做到这一点,所以有人可以告诉我是否可能吗? 这是我的功能:

def _init_from_edges(self, edges):
    self._G={}
    for e in edges:
        if e[0] in self._G:
            self._G[e[0]][e[1]]=e[2]
        else:
            self._G[e[0]]={e[1]:e[2]}

2 个答案:

答案 0 :(得分:1)

您的代码不能轻松利用dict的理解能力,因为它实际上是一个多变量(键不具有一个值,而是多个)。

您可以简化代码with collections.defaultdict

from collections import defaultdict

def _init_from_edges(self, edges):
    self._G = defaultdict(dict)
    for v1, v2, capacity in edges:
        self._G[v1][v2] = capacity
    # Optional: Remove defaultdict behaviors after building
    self._G = dict(self._G)

使用defaultdict(dict)意味着当字典中没有键时,它会立即以全新的dict创建,因此您根本不需要执行成员资格测试。

请注意,我还使用了对命名变量的解包,而不是重复索引,以使代码更具自记录性。

通过实际的dict理解才能实现此目的的唯一方法是:

  1. 为每个输入重新扫描edges一次,以收集给定v2的所有capacity / v1对(但这是O(n**2),所以很不好知道edges是否可以大)
  2. 提前将每个v1的所有值打包在一起,这样就可以一次构造所有子dict

由于选项#1通常非常浪费,因此唯一可行的方法是作为dict理解而无需一遍又一遍地重新扫描edges是选项#2,您可以 O(n log n)中对followed by itertools.groupby进行排序:

from itertools import groupby
from operator import itemgetter

def _init_from_edges(self, edges):
    self._G = {v1: {v2: capacity for _, v2, capacity in grp}
               for v1, grp in groupby(sorted(edges, key=itemgetter(0)),
                                      key=itemgetter(0))}

这需要O(n log n)来对edges进行排序(如果edges已经被排序,Python的TimSort意味着它更接近O(n)的工作),然后O(n)可以工作将结果分组。比{v1: {v2: capacity for v, v2, capacity in edges if v == v1} for v1, _, _ in edges}快,但仍比使用defaultdict的非理解方法(在所有情况下均为O(n))慢。

答案 1 :(得分:0)

这有点令人费解,但似乎可行:

edges = [(1, 2, 3),
         (1, 3, 4),
         (2, 1, 5),
         (2, 3, 6),
         (2, 2, 7)]
dico = {v1: {v2: cap for v, v2, cap in edges if v == v1} for v1, _, _ in edges}
# {1: {2: 3, 3: 4}, 2: {1: 5, 2: 7, 3: 6}}

基本上,每个v1都将edges中的每个键:值对与该v1相加。外部理解不需要v2cap,因此仅使用下划线忽略这些值。内循环需要将v1 值与外循环的值进行比较,这就是为什么使用不同名称的原因。