{"id":538,"date":"2020-10-04T18:28:22","date_gmt":"2020-10-04T22:28:22","guid":{"rendered":"http:\/\/www.laurentluce.com\/?p=538"},"modified":"2020-10-04T18:35:21","modified_gmt":"2020-10-04T22:35:21","slug":"python-deque-implementation","status":"publish","type":"post","link":"http:\/\/www.laurentluce.com\/posts\/python-deque-implementation\/","title":{"rendered":"Python deque implementation"},"content":{"rendered":"\n<p>Python deque is a double-ended queue.  You can append to both ends and pop from both ends.  The complexity of those operations amortizes to constant time.<br><br>We are going to look at the Python 3 internal implementation of deques.  It uses a linked list of blocks of 64 pointers to objects.  This reduces memory overhead since there are fewer previous and next links.<\/p>\n\n\n\n<p>Let&#8217;s create an empty deque and see what happens.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\n&gt;&gt;&gt; import collections\n&gt;&gt;&gt; d = collections.deque()\n&gt;&gt;&gt; d\ndeque(&#91;])\n<\/pre><\/div>\n\n\n<p>A linked list with a single block of 64 pointers (set to null) is created.  The right index is initialized to the center of the block (31) and is used to refer to the last object in the deque.  The left index is initialized to the center of the block plus one (32) and is used to refer to the first object in the deque.  As of now, we did not push anything to our deque so all the pointers are pointing to nothing.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"563\" height=\"282\" src=\"https:\/\/www.laurentluce.com\/wp-content\/uploads\/2020\/09\/deque_new.png\" alt=\"\" class=\"wp-image-540\" srcset=\"http:\/\/www.laurentluce.com\/wp-content\/uploads\/2020\/09\/deque_new.png 563w, http:\/\/www.laurentluce.com\/wp-content\/uploads\/2020\/09\/deque_new-300x150.png 300w\" sizes=\"(max-width: 563px) 100vw, 563px\" \/><\/figure>\n\n\n\n<p>The deque internal C structure also has a pointer to the left most block and a pointer to the right most block.  Both are currently pointing to this single block above.  There is also an optional max length attribute that can be used for bounded queues.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\ntypedef struct {\n    ...\n    block *leftblock;\n    block *rightblock;\n    Py_ssize_t leftindex;\n    Py_ssize_t rightindex;\n    ...\n    Py_ssize_t maxlen;\n    ...\n} dequeobject;\n<\/pre><\/div>\n\n\n<p>A block has a left and right link.  It also has an array of 64 pointers to objects.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\ntypedef struct BLOCK {\n    struct BLOCK *leftlink;\n    PyObject *data&#91;BLOCKLEN];\n    struct BLOCK *rightlink;\n} block;\n<\/pre><\/div>\n\n\n<p>Let&#8217;s append two objects to our deque.  We are going to use strings here labelled e1, e2, &#8230;  The append method really is append-right.  There is also append-left.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\n&gt;&gt;&gt; d.append('e1')\n&gt;&gt;&gt; d.append('e2')\n&gt;&gt;&gt; d\ndeque(&#91;'e1', 'e2'])\n<\/pre><\/div>\n\n\n<p>The right index got incremented by two (33) and still points to the last object of the deque.  The left index did not move and still points to the first object of the deque.  The diagram below shows the string objects inside the deque for simplification, while they are in reality pointed to.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"565\" height=\"276\" src=\"https:\/\/www.laurentluce.com\/wp-content\/uploads\/2020\/09\/deque_append-1.png\" alt=\"\" class=\"wp-image-546\" srcset=\"http:\/\/www.laurentluce.com\/wp-content\/uploads\/2020\/09\/deque_append-1.png 565w, http:\/\/www.laurentluce.com\/wp-content\/uploads\/2020\/09\/deque_append-1-300x147.png 300w\" sizes=\"(max-width: 565px) 100vw, 565px\" \/><\/figure>\n\n\n\n<p>The following C function is called internally.  It takes the deque object and the object to append.  The size of the deque is increased by one.  The size of the deque is what is returned when you use the Python len function.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nstatic inline int\ndeque_append_internal(dequeobject *deque, PyObject *item, ...)\n{\n    ...\n    Py_SET_SIZE(deque, Py_SIZE(deque) + 1);\n    deque-&gt;rightindex++;\n    deque-&gt;rightblock-&gt;data&#91;deque-&gt;rightindex] = item;\n    ...\n}\n<\/pre><\/div>\n\n\n<p>Let&#8217;s append strings e3 to e32<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\n&gt;&gt;&gt; for i in range(3, 33): d.append('e%d' % i)\n&gt;&gt;&gt; d\ndeque(&#91;'e1', 'e2', 'e3', 'e4', 'e5', 'e6', 'e7', 'e8', 'e9', 'e10', 'e11', 'e12', 'e13', 'e14', 'e15', 'e16', 'e17', 'e18', 'e19', 'e20', 'e21', 'e22', 'e23', 'e24', 'e25', 'e26', 'e27', 'e28', 'e29', 'e30', 'e31', 'e32'])\n<\/pre><\/div>\n\n\n<p>The right index is now pointing to the maximum index in our first block.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"604\" height=\"281\" src=\"https:\/\/www.laurentluce.com\/wp-content\/uploads\/2020\/09\/deque_append_full.png\" alt=\"\" class=\"wp-image-548\" srcset=\"http:\/\/www.laurentluce.com\/wp-content\/uploads\/2020\/09\/deque_append_full.png 604w, http:\/\/www.laurentluce.com\/wp-content\/uploads\/2020\/09\/deque_append_full-300x140.png 300w\" sizes=\"(max-width: 604px) 100vw, 604px\" \/><\/figure>\n\n\n\n<p>What happens if we append one more string?<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\n&gt;&gt;&gt; d.append('e33')\n&gt;&gt;&gt; d\ndeque(&#91;'e1', 'e2', 'e3', 'e4', 'e5', 'e6', 'e7', 'e8', 'e9', 'e10', 'e11', 'e12', 'e13', 'e14', 'e15', 'e16', 'e17', 'e18', 'e19', 'e20', 'e21', 'e22', 'e23', 'e24', 'e25', 'e26', 'e27', 'e28', 'e29', 'e30', 'e31', 'e32', 'e33'])\n<\/pre><\/div>\n\n\n<p>A new block is allocated.  The deque right index now points to the first index of the new block.  The left block pointer continues to point to the first block.  The right block pointer now points to the new block.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"930\" height=\"388\" src=\"https:\/\/www.laurentluce.com\/wp-content\/uploads\/2020\/09\/deque_append_new_block.png\" alt=\"\" class=\"wp-image-550\" srcset=\"http:\/\/www.laurentluce.com\/wp-content\/uploads\/2020\/09\/deque_append_new_block.png 930w, http:\/\/www.laurentluce.com\/wp-content\/uploads\/2020\/09\/deque_append_new_block-300x125.png 300w, http:\/\/www.laurentluce.com\/wp-content\/uploads\/2020\/09\/deque_append_new_block-768x320.png 768w\" sizes=\"(max-width: 930px) 100vw, 930px\" \/><\/figure>\n\n\n\n<p>The same internal function is called as above but an extra piece of code is executed to allocate a new block, update the block links and the deque right block pointer.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nstatic inline int\ndeque_append_internal(dequeobject *deque, PyObject *item, Py_ssize_t maxlen)\n{\n    if (deque-&gt;rightindex == BLOCKLEN - 1) {\n        block *b = newblock();\n        if (b == NULL)\n            return -1;\n        b-&gt;leftlink = deque-&gt;rightblock;\n        deque-&gt;rightblock-&gt;rightlink = b;\n        deque-&gt;rightblock = b;\n        deque-&gt;rightindex = -1;\n    }\n    Py_SET_SIZE(deque, Py_SIZE(deque) + 1);\n    deque-&gt;rightindex++;\n    deque-&gt;rightblock-&gt;data&#91;deque-&gt;rightindex] = item;\n    ...\n}\n<\/pre><\/div>\n\n\n<p>Now that we looked at the method append, we can move to the method popleft.  We can pop the first object in our deque:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\n&gt;&gt;&gt; d.popleft()\n'e1'\n<\/pre><\/div>\n\n\n<p>The left index points to the first object in our deque.  It moved up by one and is now pointing to index 33.  The string e1 has been returned.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"926\" height=\"384\" src=\"https:\/\/www.laurentluce.com\/wp-content\/uploads\/2020\/09\/deque_popleft.png\" alt=\"\" class=\"wp-image-553\" srcset=\"http:\/\/www.laurentluce.com\/wp-content\/uploads\/2020\/09\/deque_popleft.png 926w, http:\/\/www.laurentluce.com\/wp-content\/uploads\/2020\/09\/deque_popleft-300x124.png 300w, http:\/\/www.laurentluce.com\/wp-content\/uploads\/2020\/09\/deque_popleft-768x318.png 768w\" sizes=\"(max-width: 926px) 100vw, 926px\" \/><\/figure>\n\n\n\n<p>We can see in the popleft internal function that the size of the deque is also decreased by one.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nstatic PyObject *\ndeque_popleft(dequeobject *deque, PyObject *unused)\n{\n    PyObject *item;\n    ...\n    item = deque-&gt;leftblock-&gt;data&#91;deque-&gt;leftindex];\n    deque-&gt;leftindex++;\n    Py_SET_SIZE(deque, Py_SIZE(deque) - 1);\n    ...\n    return item;\n}\n<\/pre><\/div>\n\n\n<p>Let&#8217;s continue and pop all the objects in the deque left&#8217;s block.  Strings e2 to e32 have been returned.  The left index is not showed here and is now equal to 64.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"919\" height=\"384\" src=\"https:\/\/www.laurentluce.com\/wp-content\/uploads\/2020\/09\/deque_popleft_del_block-1.png\" alt=\"\" class=\"wp-image-555\" srcset=\"http:\/\/www.laurentluce.com\/wp-content\/uploads\/2020\/09\/deque_popleft_del_block-1.png 919w, http:\/\/www.laurentluce.com\/wp-content\/uploads\/2020\/09\/deque_popleft_del_block-1-300x125.png 300w, http:\/\/www.laurentluce.com\/wp-content\/uploads\/2020\/09\/deque_popleft_del_block-1-768x321.png 768w\" sizes=\"(max-width: 919px) 100vw, 919px\" \/><\/figure>\n\n\n\n<p>The left index being equal to the block length (64), the left block can be deallocated to save some memory.  The left index is set to zero.  The deque left block and right block pointers are now pointing to the same block.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nstatic PyObject *\ndeque_popleft(dequeobject *deque, PyObject *unused)\n{\n    ...    \n    if (deque-&gt;leftindex == BLOCKLEN) {\n        if (Py_SIZE(deque)) {\n            prevblock = deque-&gt;leftblock-&gt;rightlink;\n            freeblock(deque-&gt;leftblock);\n            deque-&gt;leftblock = prevblock;\n            deque-&gt;leftindex = 0;\n        }\n        ...\n    }\n    ...\n}\n<\/pre><\/div>\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"497\" height=\"281\" src=\"https:\/\/www.laurentluce.com\/wp-content\/uploads\/2020\/09\/deque_popleft_del_block_2.png\" alt=\"\" class=\"wp-image-556\" srcset=\"http:\/\/www.laurentluce.com\/wp-content\/uploads\/2020\/09\/deque_popleft_del_block_2.png 497w, http:\/\/www.laurentluce.com\/wp-content\/uploads\/2020\/09\/deque_popleft_del_block_2-300x170.png 300w\" sizes=\"(max-width: 497px) 100vw, 497px\" \/><\/figure>\n\n\n\n<p>We looked at what happens when we append to the end of the queue (append right) and when we pop from the beginning of the queue (pop left).  The methods appendleft and pop (pop right) have similar internal mechanisms.<\/p>\n\n\n\n<p>I hope you enjoyed the article.  Don&#8217;t hesitate to comment.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Python deque is a double-ended queue. You can append to both ends and pop from both ends. The complexity of those operations amortizes to constant time. We are going to look at the Python 3 internal implementation of deques. It uses a linked list of blocks of 64 pointers to objects. This reduces memory overhead&#8230; &raquo; <a class=\"read-more-link\" href=\"http:\/\/www.laurentluce.com\/posts\/python-deque-implementation\/\">read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0},"categories":[25],"tags":[19],"_links":{"self":[{"href":"http:\/\/www.laurentluce.com\/wp-json\/wp\/v2\/posts\/538"}],"collection":[{"href":"http:\/\/www.laurentluce.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.laurentluce.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.laurentluce.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.laurentluce.com\/wp-json\/wp\/v2\/comments?post=538"}],"version-history":[{"count":15,"href":"http:\/\/www.laurentluce.com\/wp-json\/wp\/v2\/posts\/538\/revisions"}],"predecessor-version":[{"id":585,"href":"http:\/\/www.laurentluce.com\/wp-json\/wp\/v2\/posts\/538\/revisions\/585"}],"wp:attachment":[{"href":"http:\/\/www.laurentluce.com\/wp-json\/wp\/v2\/media?parent=538"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.laurentluce.com\/wp-json\/wp\/v2\/categories?post=538"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.laurentluce.com\/wp-json\/wp\/v2\/tags?post=538"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}