76

So let's say I wanna make a dictionary. We'll call it d. But there are multiple ways to initialize a dictionary in Python! For example, I could do this:

d = {'hash': 'bang', 'slash': 'dot'}

Or I could do this:

d = dict(hash='bang', slash='dot')

Or this, curiously:

d = dict({'hash': 'bang', 'slash': 'dot'})

Or this:

d = dict([['hash', 'bang'], ['slash', 'dot']])

And a whole other multitude of ways with the dict() function. So obviously one of the things dict() provides is flexibility in syntax and initialization. But that's not what I'm asking about.

Say I were to make d just an empty dictionary. What goes on behind the scenes of the Python interpreter when I do d = {} versus d = dict()? Is it simply two ways to do the same thing? Does using {} have the additional call of dict()? Does one have (even negligible) more overhead than the other? While the question is really completely unimportant, it's a curiosity I would love to have answered.

verix
  • 1,703
  • 2
  • 15
  • 12
  • 1
    I found the explanation lot clearer here: https://doughellmann.com/blog/2012/11/12/the-performance-impact-of-using-dict-instead-of-in-cpython-2-7-2/ – theBuzzyCoder Mar 27 '17 at 12:17

8 Answers8

83
>>> def f():
...     return {'a' : 1, 'b' : 2}
... 
>>> def g():
...     return dict(a=1, b=2)
... 
>>> g()
{'a': 1, 'b': 2}
>>> f()
{'a': 1, 'b': 2}
>>> import dis
>>> dis.dis(f)
  2           0 BUILD_MAP                0
              3 DUP_TOP             
              4 LOAD_CONST               1 ('a')
              7 LOAD_CONST               2 (1)
             10 ROT_THREE           
             11 STORE_SUBSCR        
             12 DUP_TOP             
             13 LOAD_CONST               3 ('b')
             16 LOAD_CONST               4 (2)
             19 ROT_THREE           
             20 STORE_SUBSCR        
             21 RETURN_VALUE        
>>> dis.dis(g)
  2           0 LOAD_GLOBAL              0 (dict)
              3 LOAD_CONST               1 ('a')
              6 LOAD_CONST               2 (1)
              9 LOAD_CONST               3 ('b')
             12 LOAD_CONST               4 (2)
             15 CALL_FUNCTION          512
             18 RETURN_VALUE        

dict() is apparently some C built-in. A really smart or dedicated person (not me) could look at the interpreter source and tell you more. I just wanted to show off dis.dis. :)

Steven Huwig
  • 20,015
  • 9
  • 55
  • 79
  • 1
    Unfortunately no one could tell you what's behind `g` because of `CALL_FUNCTION 512`... – Benjamin Toueg Jul 29 '13 at 01:27
  • 5
    Have a look at this nice article by Doug Hellmann: http://doughellmann.com/2012/11/12/the-performance-impact-of-using-dict-instead-of-in-cpython-2-7-2.html – ezdazuzena Sep 08 '14 at 13:22
  • 3
    Newer python has made `{'a' : 1, 'b' : 2}` more efficient. e.g. in python 2.7.10, the disassembly replaces the `ROT_THREE; STORE_SUBSCR; DUP_TOP` instructions with `STORE_MAP`. In python 3.5.1 it gets rid of them all and just has a single `BUILD_MAP`. – danio Feb 03 '17 at 14:27
44

As far as performance goes:

>>> from timeit import timeit
>>> timeit("a = {'a': 1, 'b': 2}")
0.424...
>>> timeit("a = dict(a = 1, b = 2)")
0.889...
DNS
  • 37,249
  • 18
  • 95
  • 132
  • I get similar result for empty dictionary: 0.1usec for 'x = {}' vs 0.3usec for 'x = dict()'. – John Fouhy Mar 19 '09 at 22:08
  • Yeah; function calls are expensive. But that form is more versatile, i.e. dict(zip(... – DNS Mar 19 '09 at 22:11
  • exactly. function calls are some 80-100 times more expensive in Python, than in C. – vartec Mar 20 '09 at 08:09
  • 1
    taking exact use from the doc:http://docs.python.org/library/timeit.html import timeit t = timeit.Timer("a = {'a': 1, 'b': 2}") t.timeit() t = timeit.Timer("a = dict(a = 1, b = 2)") t.timeit() the second is four times more time consuming – DrFalk3n Oct 12 '09 at 08:10
  • lower number is better right? So the C-buildin dict() is slower than pure Python {} ? – Houman Nov 05 '14 at 13:22
31

@Jacob: There is a difference in how the objects are allocated, but they are not copy-on-write. Python allocates a fixed-size "free list" where it can quickly allocate dictionary objects (until it fills). Dictionaries allocated via the {} syntax (or a C call to PyDict_New) can come from this free list. When the dictionary is no longer referenced it gets returned to the free list and that memory block can be reused (though the fields are reset first).

This first dictionary gets immediately returned to the free list, and the next will reuse its memory space:

>>> id({})
340160
>>> id({1: 2})
340160

If you keep a reference, the next dictionary will come from the next free slot:

>>> x = {}
>>> id(x)
340160
>>> id({})
340016

But we can delete the reference to that dictionary and free its slot again:

>>> del x
>>> id({})
340160

Since the {} syntax is handled in byte-code it can use this optimization mentioned above. On the other hand dict() is handled like a regular class constructor and Python uses the generic memory allocator, which does not follow an easily predictable pattern like the free list above.

Also, looking at compile.c from Python 2.6, with the {} syntax it seems to pre-size the hashtable based on the number of items it's storing which is known at parse time.

Matt Good
  • 3,027
  • 22
  • 15
9

Basically, {} is syntax and is handled on a language and bytecode level. dict() is just another builtin with a more flexible initialization syntax. Note that dict() was only added in the middle of 2.x series.

Benjamin Peterson
  • 19,297
  • 6
  • 32
  • 39
8

Update: thanks for the responses. Removed speculation about copy-on-write.

One other difference between {} and dict is that dict always allocates a new dictionary (even if the contents are static) whereas {} doesn't always do so (see mgood's answer for when and why):

def dict1():
    return {'a':'b'}

def dict2():
    return dict(a='b')

print id(dict1()), id(dict1())
print id(dict2()), id(dict2())

produces:

$ ./mumble.py
11642752 11642752
11867168 11867456

I'm not suggesting you try to take advantage of this or not, it depends on the particular situation, just pointing it out. (It's also probably evident from the disassembly if you understand the opcodes).

Community
  • 1
  • 1
Jacob Gabrielson
  • 34,800
  • 15
  • 46
  • 64
  • 2
    That's not what is happening here. {} is still allocating a new dict (otherwise it would be very bad), but the fact that you don't keep it alive means the id can be reused after it dies (as soon as the first dict is printed). – Brian Mar 27 '09 at 13:01
  • I suspect the function call is acting differently because it is churning the id() space a bit more so a different id is obtained on the second call. – Brian Mar 27 '09 at 13:02
  • I think mgood's answer clarified things; I've updated my entry. – Jacob Gabrielson Mar 27 '09 at 18:52
4

dict() is used when you want to create a dictionary from an iterable, like :

dict( generator which yields (key,value) pairs )
dict( list of (key,value) pairs )
bobflux
  • 11,123
  • 3
  • 27
  • 27
  • 1
    Yes. And cases that can use `{…}` instead should do so because it is more direct and faster than a call to the `dict()` constructor (function calls are expensive, in Python, and why call a function that only returns something that can be built directly through the `{…}` syntax?). – Eric O. Lebigot Apr 14 '17 at 20:13
  • It has been working in Python 2 for quite a long time now. – Eric O. Lebigot Apr 26 '17 at 21:56
1

Funny usage:

def func(**kwargs):
      for e in kwargs:
        print(e)
    a = 'I want to be printed'
    kwargs={a:True}
    func(**kwargs)
    a = 'I dont want to be printed'
    kwargs=dict(a=True)
    func(**kwargs)

output:

I want to be printed
a
Montelin
  • 11
  • 1
  • 2
    Not sure if you mean funny as in 'lol' or funny as in 'bug'. Please include some sort of commentary or explanation to this as this question was answered 8 years ago, and this will leave noobs to a degree perplexed – Chris Schaller Feb 06 '18 at 14:45
-1

In order to create an empty set we should use the keyword set before it i.e set() this creates an empty set where as in dicts only the flower brackets can create an empty dict

Lets go with an example

print isinstance({},dict) 
True 
print isinstance({},set) 
False 
print isinstance(set(),set) 
True
gmuraleekrishna
  • 3,375
  • 1
  • 27
  • 45