【问题标题】:Dictionary comprehension multiple ways字典理解多种方式
【发布时间】:2020-02-28 00:23:01
【问题描述】:

以下两个python语句有什么区别?

l = [1,2,3,4]
a = {item:0 for item in l}
b = dict((item,0) for item in l)
a == b
# True

我相信第一种方法是通过 PEP 的理解来初始化字典的正确方法,但第二种方法似乎只是创建一个生成器表达式,然后从中创建一个 dict(所以它可能做完全相同的事情作为幕后的第一种方法?)。两者之间究竟有什么区别,应该优先选择哪一个?

【问题讨论】:

  • 您已经准确描述了区别,第一个直接使用 dict-comprehension 构建字典(本质上是循环的糖:result = {}; for item in l: result[item] = 0。另一个创建生成器表达式并将其传递给 @ 987654324@构造函数...最直接的方法是使用前一种方法。
  • 唯一的区别将是真正的技术差异,例如如果您将名称 dict 重新分配为其他名称,第一个将起作用。另外,第一个可能效率更高。
  • 语义上,如果我们假设没有内置名称被遮蔽,那么两者是相同的,并且具有相同的时间/空间复杂度,尽管生成器表达式可能会稍微慢一些,因为它添加不必要的迭代生成器表达式而不是直接迭代列表的层。

标签: python generator python-internals


【解决方案1】:
a = {item:0 for item in l}

直接构造一个dict,没有中间体。

b = dict((item,0) for item in l)

为列表中的每个项目生成一个元组并将其提供给dict() 构造函数。

如果没有真正深入研究生成的 Python 字节码,我怀疑是否有一种简单的方法可以找出它们之间的差异。在性能方面,它们也可能非常接近。

我要考虑的主要问题是可读性和可维护性。第一种方式只依赖于你需要的元素,不涉及中间数据类型(元组),也不直接调用类型,而是依赖语言本身来正确连接。作为奖励,它更短更简单 - 我认为使用第二个选项没有任何优势,除了可能明确使用dict,告诉其他人预期的类型是什么。但是,如果他们一开始没有从{} 那里得到这个信息,我怀疑他们无论如何都很好......

我想我会测试一下速度:

from timeit import timeit
from random import randint

l = [randint(0, 1000) for _ in range(1000)]


def first():
    return {item: 0 for item in l}


def second():
    return dict((item,0) for item in l)


print(timeit(first, number=10000))
print(timeit(second, number=10000))

结果:

0.46899440000000003
1.0817516999999999

也始终更快,因此似乎无需使用第二个选项。如果这里有什么令人惊讶的地方,那就是第二个例子的优化有多差,性能有多差。

【讨论】:

    猜你喜欢
    • 2018-11-26
    • 2016-04-08
    • 2012-03-15
    • 1970-01-01
    • 1970-01-01
    • 2021-07-24
    • 2016-09-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多