Dict() vs {} in Python, What’s the Big Deal Anyway?

Jody LeCompte
Jun 16, 2018 · 5 min read

A comparison of literal and constructor syntax for Python dictionaries

Photo by Raul Cacho Oses on Unsplash

I recently began a new collaboration in Python / Flask to gain a greater understanding of Python as a backend language for the web — a space I had not previously used Python in. After looking at dozens of example Flask apps, one bit of code began to stick out to me time after time. In conjunction with Flask’s ability to accept a dictionary as a configuration object, I kept seeing code like this:

I recognized it as being a constructor syntax based on experience with other languages; but I was left curious as to why I had not seen it before having been dabbling with Python for a few years. Is there any real difference between the two besides aesthetic preference? Let’s take a look at that.

Performance

Speed is the easiest place to start because it’s entirely objective. Either one will be faster or they will so close that it doesn’t really matter. To measure this, we will spin up a Python REPL and utilize the module. To follow along, in your terminal enter the following:

$ python
>> from timeit import timeit

Empty Dictionaries:

>>> timeit("d = dict()")
0.11915296099778061
>>> timeit("d = {}")
0.034184868832198845

This was pretty surprising to me. At least in the case of empty dictionaries, it appears as though the literal syntax is round about 3 times as fast as using the constructor syntax. Let’s see if the same applies for non-empty dictionaries.

Populated Dictionaries

>>> timeit("d = dict(one='one',two='two',three='three',four='four',five='five',six='six')")
0.5600968430762805

>>> timeit("d = {'one':'one','two':'two','three':'three','four':'four','five':'five','six':'six'}")
0.1884483945050306

Once again, the literal syntax is in the lead with an average execution time of roughly 3x faster as well making the literal syntax the clear winner when only considering raw performance time.

Quick aside: I feel obligated to mention, that while it does appear the literal syntax is significantly faster, I caution against grepping every instance of in your codebase expecting significant increases in performance. There are some edge cases where it might matter, but in most cases it will not. Even at 2–3x faster, we’re still also talking about operations that take less than a millisecond to complete. Be wary of micro optimizations as they rarely pay off in the end.

Syntactical Differences

The syntactical differences are minor and mostly aesthetic in nature only, but for a language like Python that has such a focus on beautiful code, it seems appropriate to take a look at the differences.

Constructor Syntax:
There is one difference in the constructor syntax not shown here and that’s it being able to accept an iterator, but we’ll talk about that a little later when we compare functionality options.

The constructor syntax relies on receiving named arguments where the name of the argument also dictates the name of the key in the final dictionary, which also leads to another detail we will discuss later. One potential reason people prefer the constructor syntax is because quotes are not required around the key names and curly braces are not required which would otherwise be rare in Python code. The use of named arguments leads to another functionality item we will discuss shortly.

Literal Syntax:
The literal syntax is much more familiar to people who write code in JavaScript being wrapped in curly braces alone with each key being separated from it’s value by a colon; but may still cause friction for Ruby or PHP programmers used to using the fat arrow (=>) for assignment in maps or arrays respectively. Unlike the constructor syntax, key names must be surrounded by quotes.

I personally prefer the literal syntax aesthetically, and I assume the majority do based on the code I’ve read and it taking over a year to be exposed to constructor syntax in the wild, but that’s purely speculation. Which do you prefer? Let me know why in the comments below!

Functional Differences

While we discussed the performance and the syntax so far, there are actually a few functionality differences between the two that is worth checking out.

Constructor Syntax

The constructor syntax is capable of also accepting a list of key-value pairs as tuples, or accepting a generator that returns such. Take this piece of code for example

Here we create a function that returns a generator that yields the next tuple in the list on each call; we have instead passed that generator to and if we were to print then this would be our output:

{'key3': 'value3', 'key2': 'value2', 'key1': 'value1'}

On the flip side, if you use the named argument approach with as opposed to passing it a sequence of key-value pairs expressed as a tuple, you are not able to create a numerically indexed dictionary. This is the trade off that I mentioned before. Overall, the constructor syntax is much more flexible.

Literal Syntax

The literal syntax allows for numerically indexed dictionaries without any ceremony, unlike the constructor syntax, but the real key difference is the ability to use the literal syntax for dictionary comprehensions to quickly and explicitly build or transform a dictionary like the code example below.

Taking a Look at Opcodes

Opcodes, short for operation codes, are out of the scope of this article and quite frankly a little out of the scope of my knowledge on Python. But in short, opcodes are the machine language commands your code is being interpreted down to. That said, I still thought it might be fun to use the python disassembler to take a look and see if there’s a large difference here between the two.

In your Python REPL, let’s import the Python disassembler and test empty dictionaries.

>>> from dis import dis
>>> dis('{}')
0 BUILD_MAP 0
2 RETURN_VALUE
>>> dis('dict()')
1 0 LOAD_NAME 0 (dict)
2 CALL_FUNCTION 0
4 RETURN_VALUE

For empty dictionaries, it looks like things are about the same with the overhead of calling the function. Let’s take a look at populated dictionaries.

>>> dis("{'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}")
0 LOAD_CONST 0 (1)
2 LOAD_CONST 1 (2)
4 LOAD_CONST 2 (3)
6 LOAD_CONST 3 (4)
8 LOAD_CONST 4 (5)
10 LOAD_CONST 5 (('a', 'b', 'c', 'd', 'e'))
12 BUILD_CONST_KEY_MAP 5
14 RETURN_VALUE
>>> dis("dict(a=1, b=2, c=3, d=4, e=5)")
0 LOAD_NAME 0 (dict)
2 LOAD_CONST 0 (1)
4 LOAD_CONST 1 (2)
6 LOAD_CONST 2 (3)
8 LOAD_CONST 3 (4)
10 LOAD_CONST 4 (5)
12 LOAD_CONST 5 (('a', 'b', 'c', 'd', 'e'))
14 CALL_FUNCTION_KW 5
16 RETURN_VALUE

Again we see only a very small difference in the opcodes produced between the the literal and constructor syntax. It poses the question, what exact causes that huge performance difference?

Conclusion

Ultimately, because the difference in speed would not be likely to create a huge difference in overall application performance, the impression I formed was that it mostly comes down to preference around the syntax and the couple of functional differences. I prefer the literal syntax myself, how about you?

Thank you for taking the time to read this and please share any comments or questions below.

Jody LeCompte

Written by

Father of two boys, developer, writer, and fisherman. I utilize a wide range of technology and occassionally write about it.