【问题标题】:Is there a difference between using a dict literal and a dict constructor?使用 dict 文字和 dict 构造函数之间有区别吗?
【发布时间】:2011-09-30 10:28:18
【问题描述】:

使用 PyCharm,我注意到它可以转换 dict 文字

d = {
    'one': '1',
    'two': '2',
}

进入 dict 构造函数

d = dict(one='1', two='2')

这些不同的方法在某些方面是否存在显着差异?

(在写这个问题时,我注意到使用dict() 似乎无法指定数字键.. d = {1: 'one', 2: 'two'} 是可能的,但显然dict(1='one' ...) 不是。还有什么?)

【问题讨论】:

  • dict() 采用键值对列表并允许命名参数,因此它可以用于创建任何类型的字典,而不是使用您使用的语法。 pyCharm 中存在一个错误 (youtrack.jetbrains.net/issue/PY-2512) 也可能毫无价值,特别是因为您发现了什么,该错误已得到修复。
  • 相关:stackoverflow.com/questions/5790860/…(总结:PyCharm的行为更慢更丑)
  • 显然 CPython 2.7 dict() 更慢(慢 6 倍?)。请参阅:doughellmann.com/2012/11/… 无论如何,我开始更喜欢构造函数语法,因为我发现在字典和函数调用之间键入和移动代码更容易。
  • 不要忘记空格:您不能使用第二种方式创建包含空格的键。但是,第一种方法可以使用任何字符串,它不会在意。当然,这同样适用于 Unicode。
  • 在 Python 2 中,dict(abc = 123) 构造函数生成一个带有字节字符串键 'abc' 的字典,如果您使用 unicode_literals 并期望字典键是 unicode u'abc',这可能会令人惊讶.见stackoverflow.com/questions/20357210/…

标签: python dictionary pycharm


【解决方案1】:

我认为您已经指出了最明显的区别。除此之外,

第一个不需要查找dict,这应该会更快一点

第二个在locals() 中查找dict,然后在globals() 中查找内置函数,因此您可以通过定义一个名为dict 的本地函数来切换行为,尽管我想不出任何地方除了调试时可能是个好主意

【讨论】:

  • 本地调用 dict 可能有用的示例:stackoverflow.com/a/7880276/313113
  • 我相信使用 dict() 也会首先为 dict() 的参数构造一个 dict,然后为要创建的实际 dict 实例创建第二个 dict。 Braces 一步创建 dict 实例。
  • dict 可以与zip 结合,将两个并行(即相关)数组转换为单个关联映射。
【解决方案2】:

文字要快得多,因为它使用优化的 BUILD_MAP 和 STORE_MAP 操作码,而不是通用的 CALL_FUNCTION:

> python2.7 -m timeit "d = dict(a=1, b=2, c=3, d=4, e=5)"
1000000 loops, best of 3: 0.958 usec per loop

> python2.7 -m timeit "d = {'a':1, 'b':2, 'c':3, 'd':4, 'e':5}"
1000000 loops, best of 3: 0.479 usec per loop

> python3.2 -m timeit "d = dict(a=1, b=2, c=3, d=4, e=5)"
1000000 loops, best of 3: 0.975 usec per loop

> python3.2 -m timeit "d = {'a':1, 'b':2, 'c':3, 'd':4, 'e':5}"
1000000 loops, best of 3: 0.409 usec per loop

【讨论】:

  • @Ned:大多数情况下,对于大多数用户而言,这根本不重要,但在某些情况下,正在创建数百万或数十亿这样的数据,并且 2 倍的加速是有意义的。
  • @MrFooz:有这样的情况。我想你会发现 99.9% 的人在做微计时都不在这些情况下。
  • @Ned 这在一个线程中是相关的,询问哪个更快。
  • @Elliot OP 没有问哪个更快。
  • 如果您从源代码中的 dict 文字生成数百万个字典或一个具有数百万个键的字典,那么您做错了。
【解决方案3】:

它们在 Python 3.2 上看起来几乎相同。

正如 gnibbler 指出的那样,第一个不需要查找 dict,这应该会稍微快一点。

>>> def literal():
...   d = {'one': 1, 'two': 2}
...
>>> def constructor():
...   d = dict(one='1', two='2')
...
>>> import dis
>>> dis.dis(literal)
  2           0 BUILD_MAP                2
              3 LOAD_CONST               1 (1)
              6 LOAD_CONST               2 ('one')
              9 STORE_MAP
             10 LOAD_CONST               3 (2)
             13 LOAD_CONST               4 ('two')
             16 STORE_MAP
             17 STORE_FAST               0 (d)
             20 LOAD_CONST               0 (None)
             23 RETURN_VALUE
>>> dis.dis(constructor)
  2           0 LOAD_GLOBAL              0 (dict)
              3 LOAD_CONST               1 ('one')
              6 LOAD_CONST               2 ('1')
              9 LOAD_CONST               3 ('two')
             12 LOAD_CONST               4 ('2')
             15 CALL_FUNCTION          512
             18 STORE_FAST               0 (d)
             21 LOAD_CONST               0 (None)
             24 RETURN_VALUE

【讨论】:

  • 请注意,在某些实现中,这并不是真正的“微小”,更像是 100 的因子:$ pypy -m perf timeit -l '1000000' -n '5' -s 'i=(("a",1), ("b", 2), ("c", 3))' "{'a': 1, 'b': 2, 'c': 3}" ....... Mean +- std dev: 1.73 ns +- 0.14 ns $ pypy -m perf timeit -l '1000000' -n '5' -s 'i=(("a",1), ("b", 2), ("c", 3))' '{k:v for k,v in i}' ....... Mean +- std dev: 139 ns +- 10 ns $ pypy -m perf timeit -l '1000000' -n '5' -s 'i=(("a",1), ("b", 2), ("c", 3))' 'dict(i)' ....... Mean +- std dev: 188 ns +- 16 ns
【解决方案4】:

这两种方法生成相同的字典,但正如您所指出的,Python 的词法规则会干扰。

字典文字更明显是字典,你可以创建任何类型的键,但你需要引用键名。另一方面,如果出于某种原因需要,您可以使用变量作为键:

a = "hello"
d = {
    a: 'hi'
    }

dict() 构造函数为您提供了更大的灵活性,因为它采用多种形式的输入。例如,您可以为它提供一个对的迭代器,它会将它们视为键/值对。

我不知道为什么 PyCharm 会提供将一种形式转换为另一种形式。

【讨论】:

  • 好吧,我猜 PyCharm 只是想变得更好。就像它似乎总是提供将单引号字符串转换为双引号一样 - 没有明显的原因。
  • 如果你的键是字符串,你只需要引用你的键。它们也可以很容易地成为frozensets of floats 的元组,尽管这可能有点难看。
【解决方案5】:

与 python 3.4 + pycharm 的一大区别是 dict() 构造函数 如果键的数量超过 256,则会产生“语法错误”消息。

我现在更喜欢使用 dict 字面量。

【讨论】:

  • 不只是 python 3.4。这是因为 CPython stackoverflow.com/a/8932175/2718295)
【解决方案6】:

来自python 2.7教程:

一对大括号创建一个空 字典:{}。放置一个 逗号分隔的 key:value 列表 大括号内的对添加初始 键:值对到字典; 这也是字典的方式 写在输出上。

tel = {'jack': 4098, 'sape': 4139}
data = {k:v for k,v in zip(xrange(10), xrange(10,20))}

同时:

dict() 构造函数构建 直接来自列表的字典 存储为元组的键值对。什么时候 这些对形成一个模式,列出 推导式可以紧凑地指定 键值列表。

tel = dict([('sape', 4139), ('guido', 4127), ('jack', 4098)]) {'sape': 4139, 'jack': 4098, 'guido': 4127}
data = dict((k,v) for k,v in zip(xrange(10), xrange(10,20)))

当键是简单的字符串时,它 有时更容易指定对 使用关键字参数:

dict(sape=4139, guido=4127, jack=4098)
>>>  {'sape': 4139, 'jack':4098, 'guido': 4127}

所以 {} 和 dict() 都生成字典,但提供了一些不同的字典数据初始化方式。

【讨论】:

    【解决方案7】:

    我发现 dict 文字 d = {'one': '1'} 更具可读性,您定义数据,而不是分配值并将它们发送到 dict() 构造函数。

    另一方面,我看到人们将 dict 文字错误地输入为d = {'one', '1'},这在现代 python 2.7+ 中会创建一个集合。

    尽管如此,我仍然更喜欢使用集合文字,因为我认为它更具可读性,个人偏好。

    【讨论】:

    • 我经常忘记sets 的字面语法存在。我希望有一个用于有序 dicts 的字面语法……很确定我比集合更频繁地使用它们。
    【解决方案8】:

    当您从其他东西(无 python)复制粘贴值时,dict() 文字很好 例如环境变量列表。 如果你有一个 bash 文件,说

    FOO='bar'
    CABBAGE='good'
    

    您可以轻松地将其粘贴到 dict() 文字中并添加 cmets。它还可以更容易地做相反的事情,复制到其他东西中。而{'FOO': 'bar'} 语法对于 python 和 json 来说是非常独特的。因此,如果您经常使用 json,您可能希望使用带有双引号的 {} 文字。

    【讨论】:

      【解决方案9】:

      没有 dict 字面量来创建 dict 继承的类、带有附加方法的自定义 dict 类。在这种情况下,应使用自定义 dict 类构造函数,例如:

      class NestedDict(dict):
      
          # ... skipped
      
      state_type_map = NestedDict(**{
          'owns': 'Another',
          'uses': 'Another',
      })
      

      【讨论】:

        【解决方案10】:

        还要考虑这样一个事实,即与运算符匹配的标记不能在构造函数语法中使用,即破折号键。

        >>> dict(foo-bar=1)
        File "<stdin>", line 1
        SyntaxError: keyword can't be an expression
        
        >>> {'foo-bar': 1}
        {'foo-bar': 1}
        

        【讨论】:

          【解决方案11】:

          在这里聚会超级迟到,但如果你有一个 kwargs 功能:

          def foo(a=None, b=None):
              ...
          

          然后你像这样喷dict

          d_1 = { 'a': 1, 'b': 2 }
          d_2 = dict(a=1, b=2)
          
          # This works
          foo(**d_1)
          
          # And this as well
          foo(**d_2)
          

          d_2 可能更适合重构可能在您的foo 签名中更改的参数名称。因为在d_1 中它们是字符串。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-02-14
            • 2016-03-26
            • 2016-09-02
            • 1970-01-01
            • 2018-05-18
            • 1970-01-01
            • 2022-01-26
            • 2013-10-07
            相关资源
            最近更新 更多