【问题标题】:Create dict by successive updates in a comprehension通过理解中的连续更新创建 dict
【发布时间】:2019-12-27 19:18:11
【问题描述】:

我有一个输出字典的函数:

def mkdict(i):
    # Simplified example
    return {i: i, 2*i: 2*i}

我正在使用这个函数构建一个dict

result = {}
for i in range(5):
    result.update(mkdict(i))
print(result)
# result = {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 6: 6, 8: 8}

有没有一种方法可以使用理解在一行中生成result

类似

result = {mkdict(i) for i in range(5)}
result = {**mkdict(i) for i in range(5)}

【问题讨论】:

  • 这将是一个很好的语法添加,每次使用 * 创建一个包含少量元素的平面列表也是如此,让我们在 Python 3.9 中实现它:P

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


【解决方案1】:

理解(list、set、dict 和生成器表达式)都按照处理单个项目的原则工作。产生两个键值对的函数不是单个项目,因此您需要将结果拆分为单个项目。理解不支持PEP 448 unpacking syntax,只适用于非理解形式。

您必须在返回的字典的 dict.items() 上添加一个额外的循环:

{k: v for i in range(5) for k, v in mkdict(i).items()}

您也可以使用 dict() 类型,因为它接受可迭代的键值对:

dict(item for i in range(5) for item in mkdict(i).items())

你也可以使用itertools.chain.from_iterable():

from itertools import chain

dict(chain.from_iterable(mkdict(i).items() for i in range(5)))

如果mkdict() 是用编译语言实现的(来自库或用 Cython 等实现)并且迭代次数足够大,则可能值得降低可读性并使用map() function 申请范围值的mkdict() 函数和生成的每个字典的未绑定dict.items 方法:

dict(chain.from_iterable(map(dict.items, map(mkdict, range(5)))))

在这里,这并没有太大的可能性,因为mkdict() 是在 Python 中实现的,因此map() 必须回调 Python 解释器来获取字典。但是对于不这样做的函数,上面的表达式完全在本机机器代码中执行迭代和字典处理,完全避免了 Python 解释器循环。

每个选项的演示:

>>> def mkdict(i):
...     # Simplified example
...     return {i: i, 2*i: 2*i}
...
>>> {k: v for i in range(5) for k, v in mkdict(i).items()}
{0: 0, 1: 1, 2: 2, 4: 4, 3: 3, 6: 6, 8: 8}
>>> dict(item for i in range(5) for item in mkdict(i).items())
{0: 0, 1: 1, 2: 2, 4: 4, 3: 3, 6: 6, 8: 8}
>>> from itertools import chain
>>> dict(chain.from_iterable(mkdict(i).items() for i in range(5)))
{0: 0, 1: 1, 2: 2, 4: 4, 3: 3, 6: 6, 8: 8}
>>> dict(chain.from_iterable(map(dict.items, map(mkdict, range(5)))))
{0: 0, 1: 1, 2: 2, 4: 4, 3: 3, 6: 6, 8: 8}

请注意,PEP 448 明确排除了推导式,但未来的 Python 版本可以为该语言添加类似的内容。引用自PEP abstract section

此 PEP 不包括列表、集合和字典推导中的解包运算符,尽管未来的提议并未排除这一点。

【讨论】:

  • 谢谢。总的来说,为了便于阅读,我倾向于保留最初的 3 行形式,但感谢您的详细解释。我正在寻找某种对{**a, **b, **c,...} 形式的概括。
  • @Jérôme:这种形式仅适用于 key-datum 列表,这是您可以生成字典的两种方法之一(另一种是字典理解)。请参阅reference documentation
【解决方案2】:

利用functools.reduce 反复将输入字典缩减为一个。

举个例子:

def mkdict(i):
    return {i: i, 2*i: 2*i}

dicts = [mkdict(i) for i in range(5)]

以下代码(有点优雅的单行代码):

from functools import reduce

result = reduce(lambda a, b: {**a, **b}, dicts)

产生{0: 0, 1: 1, 2: 2, 4: 4, 3: 3, 6: 6, 8: 8} - 请注意序列中后面的字典会覆盖之前的键。

【讨论】:

    猜你喜欢
    • 2015-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-30
    • 2018-01-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多