246

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

sophros
  • 14,672
  • 11
  • 46
  • 75
maligree
  • 5,939
  • 10
  • 34
  • 51
  • 8
    `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 (http://youtrack.jetbrains.net/issue/PY-2512) in pyCharm specifically because of what you discovered, which has been fixed). – Wooble Jul 07 '11 at 12:40
  • 1
    related: http://stackoverflow.com/questions/5790860/and-vs-list-and-dict-which-is-better (summary: PyCharm's behavior is slower and uglier) – Wooble Jul 07 '11 at 13:04
  • 1
    Apparently CPython 2.7 dict() is slower (6 times slower?). See: http://doughellmann.com/2012/11/the-performance-impact-of-using-dict-instead-of-in-cpython-2-7-2.html 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. – David Wheaton Mar 05 '13 at 20:51
  • 3
    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. – CamilB Aug 27 '13 at 09:48
  • 2
    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 http://stackoverflow.com/questions/20357210/how-do-i-tell-dict-in-python-2-to-use-unicode-instead-of-byte-string. – Li-aung Yip Feb 21 '15 at 14:57
  • @CamilB you can always pass **kwargs to constructor. This, however, requires yet another dict literal to be instantiated beforehand, so it has limited usecases (but it can be used as a sort of "pseudo-partial" pattern).. – cowbert Sep 13 '17 at 16:42
  • Isn't it a bit of a flaw that one can use the one for space-containing keys, but not the other..? – jtlz2 Jan 26 '23 at 10:48

13 Answers13

137

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

John La Rooy
  • 295,403
  • 53
  • 369
  • 502
  • 4
    An example of where a local called dict might be useful: http://stackoverflow.com/a/7880276/313113 – Alex Bitek Oct 27 '14 at 08:53
  • 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. – NeilG May 14 '19 at 12:59
  • 1
    `dict` could be combined with `zip` to convert two parallel (i.e related) arrays into a single association mapping. – jpaugh Nov 09 '20 at 15:39
77

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
Daniel Kluev
  • 11,025
  • 2
  • 36
  • 36
  • 11
    @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. – Mr Fooz Dec 04 '12 at 00:20
  • 5
    @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 Batchelder Dec 04 '12 at 16:56
  • 31
    @Ned It's pertinent in a thread asking which one is faster though. – Elliott Dec 09 '12 at 03:52
  • 12
    @Elliot The OP didn't ask which one is faster. – Tom Ferguson Jul 01 '14 at 15:09
  • 1
    I don't understand people's fascination with deliberating over the fascination of deciding which technique is microscopically faster :D – modulitos Aug 15 '15 at 02:41
  • @Ned Batchelder Twice as fast is not "microscopically faster" - that's a significant difference. – J. Taylor Sep 12 '15 at 08:04
  • 2
    @JesseTaylor In a real program, it's extremely unlikely that this difference will be significant. It's "twice as fast", but it's also only half a microsecond faster. – Ned Batchelder Sep 12 '15 at 12:24
  • 7
    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. – jwg Apr 25 '17 at 16:17
  • 1
    Note that the difference will vanish if you make a bigger dict, when the constructor won't matter that much. – styrofoam fly Mar 01 '18 at 19:16
  • 1
    While it is true the performance doesn't matter for most cases, it is still another point for literals, in an area where there are not really any important differences for many cases. Therefore, even something borderline irrelevant may help argue why to do things one way (though perhaps the time spent debating it was wasted in all practicaclity, but who say's we can't debate whatever we feel like debating) – csga5000 Mar 23 '18 at 20:14
  • 1
    This blog post is great: https://doughellmann.com/blog/2012/11/12/the-performance-impact-of-using-dict-instead-of-in-cpython-2-7-2/ (Python 2.7). Note that one thing you may want to avoid is passing kwargs to the `dict` constructor as CPython will build the dictionary twice. Passing an iterable is more efficient, but still has some overhead. I still use the `dict` constructor for legibility and maintainability, but be careful in a hot loop. – DylanYoung Mar 11 '19 at 14:49
  • Note also, that in certain implementations, this is far from a "micro-optimisation": on PyPy for example, it's roughly 100x more performant to use a dict literal instead of the constructor (PyPy 5.8.0). – DylanYoung Mar 11 '19 at 15:05
41

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
Paolo Moretti
  • 54,162
  • 23
  • 101
  • 92
  • 1
    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``` – DylanYoung Mar 11 '19 at 15:11
  • I'm not quite sure what you mean by "look pretty much the same" - the `LOAD_CONST`s for the arguments and the fact they have a `RETURN_VALUE` are the same (as they would be in pretty much any function), but the the actual work done in the middle is entirely different (based on `BUILD_MAP` and `STORE_MAP` in the literal approach, and `LOAD_GLOBAL` and `CALL_FUNCTION`) in the constructor approach. I would assume that the second approach then does the `BUILD_MAP` and `STORE_MAP` work in a second frame that we don't see here. – yoniLavi Jun 19 '23 at 09:51
14

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.

Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
  • 2
    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. – maligree Jul 07 '11 at 12:53
  • 1
    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. – Wooble Jul 07 '11 at 13:03
8

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.

Michel Boiron
  • 81
  • 1
  • 2
  • 4
    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. (https://stackoverflow.com/a/8932175/2718295) – cowbert Dec 04 '17 at 20:34
7

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.

Artsiom Rudzenka
  • 27,895
  • 4
  • 34
  • 52
3

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.

lee penkman
  • 1,160
  • 15
  • 19
  • I regularly forget that the literal syntax for `set`s exist. I wish there was a literal syntax for ordered dicts... pretty sure I use them more often than sets. – ArtOfWarfare Jan 28 '16 at 01:27
3

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.

Nick Humrich
  • 14,905
  • 8
  • 62
  • 85
2

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',
})
Dmitriy Sintsov
  • 3,821
  • 32
  • 20
1

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.

Jonathan Nazario
  • 133
  • 3
  • 13
1

Also, when it comes to reading code (which is a lot), I feel that the literal has less cognitive burden (because we associate literals to be something that is less "active" than say a dict() which looks like a function call which makes the brain wonder...at least for a micro second :)) compared to dict(), admittedly partly due to the syntax highlighting in the editor, but still very relevant on a daily basis (at least for me).

If I focus on the surrounding code around the dict statement represented as ..., a literal dict makes it a bit easier for me to understand the surrounding code :).

...
...
...
config = {
    'key1':"value1",
    'key2':"value2",
    'key3':"value3",
    'key4':"value4",
}
...
...
...

#****VS *****

...
...
...
config =dict(
    key1 = 'value1',
    key2 = 'value2',
    key3 = 'value3',
    key4 = 'value4',

)
...
...
...

Anwar Husain
  • 1,414
  • 14
  • 19
0

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}
Brian Whitton
  • 364
  • 1
  • 10
0

Performance aside (since the performance difference isn't much), I use the dict function for when the dictionary is intended for passing keyword arguments. This way, it ensures whatever you put in the dict is a valid key for a keyword argument, it cannot start with a number, cannot contain special characters, etc.

For example:

def foo(a=0, b=0):
    return a + b

def bar(a=0, b=0):
    return a - b

def apply(funcs, kwargs):
    return [func(**kwargs) for func in funcs]

>>> apply([foo, bar], dict(a=10, b=20))
[30, -10]
rabbit.aaron
  • 2,369
  • 16
  • 25