ChatGPT解决这个技术问题 Extra ChatGPT

Is there a difference between using a dict literal and a dict constructor?

Using PyCharm, I noticed it offers to convert a dict literal:

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

into a dict constructor:

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

Do these different approaches differ in some significant way?

(While writing this question I noticed that using dict() it seems impossible to specify a numeric key .. d = {1: 'one', 2: 'two'} is possible, but, obviously, dict(1='one' ...) is not. Anything else?)

dict() takes a list of key-value pairs as well as allowing named parameters, so it can use used to create any type of dict, just not with the syntax you're using. It's also probably worth nothing that there was a bug (youtrack.jetbrains.net/issue/PY-2512) in pyCharm specifically because of what you discovered, which has been fixed).
related: stackoverflow.com/questions/5790860/… (summary: PyCharm's behavior is slower and uglier)
Apparently CPython 2.7 dict() is slower (6 times slower?). See: doughellmann.com/2012/11/… In any case I am starting to prefer the constructor syntax anyways since I find it easier to type and move code between dicts and function calls.
Don't forget spaces: you can't create keys that contain spaces using the second way. The first way, though, can take any string, it won't care. The same applies to Unicode, of course.
In Python 2, the dict(abc = 123) constructor produces a dictionary with byte-string keys 'abc', which may be surprising if you are using unicode_literals and expecting dictionary keys to be unicode u'abc'. See stackoverflow.com/questions/20357210/….

J
John La Rooy

I think you have pointed out the most obvious difference. Apart from that,

the first doesn't need to lookup dict which should make it a tiny bit faster

the second looks up dict in locals() and then globals() and the finds the builtin, so you can switch the behaviour by defining a local called dict for example although I can't think of anywhere this would be a good idea apart from maybe when debugging


An example of where a local called dict might be useful: stackoverflow.com/a/7880276/313113
I believe also using dict() will first construct a dict for the arguments to dict() and will then create a second dict for the actual dict instance to be created. Braces creates the dict instance in one step.
dict could be combined with zip to convert two parallel (i.e related) arrays into a single association mapping.
D
Daniel Kluev

Literal is much faster, since it uses optimized BUILD_MAP and STORE_MAP opcodes rather than generic 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: Most of the time for most users it doesn't matter at all, but there are situations where millions or billions of these are being created and a 2x speedup is meaningful.
@MrFooz: there are situations like that. I think you'll find that 99.9% of the people doing micro-timings are not in those situations.
@Ned It's pertinent in a thread asking which one is faster though.
@Elliot The OP didn't ask which one is faster.
If you are producing either millions of dicts or one dict with millions of keys, from dict literals in your source, you're doing it wrong.
P
Paolo Moretti

They look pretty much the same on Python 3.2.

As gnibbler pointed out, the first doesn't need to lookup dict, which should make it a tiny bit faster.

>>> 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

Note that in some implementations, this isn't really a "tiny bit", more like a factor of 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
N
Ned Batchelder

These two approaches produce identical dictionaries, except, as you've noted, where the lexical rules of Python interfere.

Dictionary literals are a little more obviously dictionaries, and you can create any kind of key, but you need to quote the key names. On the other hand, you can use variables for keys if you need to for some reason:

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

The dict() constructor gives you more flexibility because of the variety of forms of input it takes. For example, you can provide it with an iterator of pairs, and it will treat them as key/value pairs.

I have no idea why PyCharm would offer to convert one form to the other.


Well, I guess PyCharm is just trying to be extra nice. Just as it always seems to offer to convert single-quoted strings into double-quoted -- for no apparent reason.
You only need to quote your keys if your keys are strings. They could just as easily be tuples of frozensets of floats, although this might get a bit ugly.
A
Artsiom Rudzenka

From python 2.7 tutorial:

A pair of braces creates an empty dictionary: {}. Placing a comma-separated list of key:value pairs within the braces adds initial key:value pairs to the dictionary; this is also the way dictionaries are written on output.

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

While:

The dict() constructor builds dictionaries directly from lists of key-value pairs stored as tuples. When the pairs form a pattern, list comprehensions can compactly specify the key-value list.

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)))

When the keys are simple strings, it is sometimes easier to specify pairs using keyword arguments:

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

So both {} and dict() produce dictionary but provide a bit different ways of dictionary data initialization.


M
Michel Boiron

One big difference with python 3.4 + pycharm is that the dict() constructor produces a "syntax error" message if the number of keys exceeds 256.

I prefer using the dict literal now.


It's not just python 3.4. This is because CPython < 3.7 has a maximum number of 255 literal arguments passed to a callable. (stackoverflow.com/a/8932175/2718295)
l
lee penkman

I find the dict literal d = {'one': '1'} to be much more readable, your defining data, rather than assigning things values and sending them to the dict() constructor.

On the other hand i have seen people mistype the dict literal as d = {'one', '1'} which in modern python 2.7+ will create a set.

Despite this i still prefer to all-ways use the set literal because i think its more readable, personal preference i suppose.


I regularly forget that the literal syntax for sets exist. I wish there was a literal syntax for ordered dicts... pretty sure I use them more often than sets.
N
Nick Humrich

the dict() literal is nice when you are copy pasting values from something else (none python) For example a list of environment variables. if you had a bash file, say

FOO='bar'
CABBAGE='good'

you can easily paste then into a dict() literal and add comments. It also makes it easier to do the opposite, copy into something else. Whereas the {'FOO': 'bar'} syntax is pretty unique to python and json. So if you use json a lot, you might want to use {} literals with double quotes.


D
Dmitriy Sintsov

There is no dict literal to create dict-inherited classes, custom dict classes with additional methods. In such case custom dict class constructor should be used, for example:

class NestedDict(dict):

    # ... skipped

state_type_map = NestedDict(**{
    'owns': 'Another',
    'uses': 'Another',
})

B
Brian Whitton

Also consider the fact that tokens that match for operators can't be used in the constructor syntax, i.e. dasherized keys.

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

>>> {'foo-bar': 1}
{'foo-bar': 1}

J
Jonathan Nazario

Super late to the party here, but if you have a kwargs function:

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

And your splatting a dict like so:

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)

But d_2 may be better suited for refactoring argument names that may change in your foo signature. Since in d_1 they are strings.