list() uses more memory than list comprehension
<p>So i was playing with <code>list</code> objects and found little strange thing that if <code>list</code> is created with <code>list()</code> it uses more memory, than list comprehension? I'm using Python 3.5.2</p>

<pre><code>In [1]: import sys
In [2]: a = list(range(100))
In [3]: sys.getsizeof(a)
Out[3]: 1008
In [4]: b = [i for i in range(100)]
In [5]: sys.getsizeof(b)
Out[5]: 912
In [6]: type(a) == type(b)
Out[6]: True
In [7]: a == b
Out[7]: True
In [8]: sys.getsizeof(list(b))
Out[8]: 1008
</code></pre>

<p>From the <a href="https://docs.python.org/3.5/library/stdtypes.html#list">docs</a>:</p>

<blockquote>
  <p>Lists may be constructed in several ways:</p>
  
  <ul>
  <li>Using a pair of square brackets to denote the empty list: <code>[]</code></li>
  <li>Using square brackets, separating items with commas: <code>[a]</code>, <code>[a, b, c]</code></li>
  <li>Using a list comprehension: <code>[x for x in iterable]</code></li>
  <li>Using the type constructor: <code>list()</code> or <code>list(iterable)</code></li>
  </ul>
</blockquote>

<p>But it seems that using <code>list()</code> it uses more memory.</p>

<p>And as much <code>list</code> is bigger, the gap increases.</p>

<p><a href="https://i.stack.imgur.com/VVHJL.png"><img src="https://i.stack.imgur.com/VVHJL.png" alt="Difference in memory"></a></p>

<p>Why this happens?</p>

<p><strong>UPDATE #1</strong></p>

<p>Test with Python 3.6.0b2:</p>

<pre><code>Python 3.6.0b2 (default, Oct 11 2016, 11:52:53) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
&gt;&gt;&gt; import sys
&gt;&gt;&gt; sys.getsizeof(list(range(100)))
1008
&gt;&gt;&gt; sys.getsizeof([i for i in range(100)])
912
</code></pre>

<p><strong>UPDATE #2</strong></p>

<p>Test with Python 2.7.12:</p>

<pre><code>Python 2.7.12 (default, Jul  1 2016, 15:12:24) 
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
&gt;&gt;&gt; import sys
&gt;&gt;&gt; sys.getsizeof(list(xrange(100)))
1016
&gt;&gt;&gt; sys.getsizeof([i for i in xrange(100)])
920
</code></pre>

<p>I think you're seeing over-allocation patterns this is a <a href="https://github.com/python/cpython/blob/3.5/Objects/listobject.c#L42" rel="nofollow">sample from the source</a>:</p>

<pre><code>/* This over-allocates proportional to the list size, making room
 * for additional growth.  The over-allocation is mild, but is
 * enough to give linear-time amortized behavior over a long
 * sequence of appends() in the presence of a poorly-performing
 * system realloc().
 * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
 */

new_allocated = (newsize &gt;&gt; 3) + (newsize &lt; 9 ? 3 : 6);
</code></pre>

<hr>

<p>Printing the sizes of list comprehensions of lengths 0-88 you can see the pattern matches:</p>

<pre><code># create comprehensions for sizes 0-88
comprehensions = [sys.getsizeof([1 for _ in range(l)]) for l in range(90)]

# only take those that resulted in growth compared to previous length
steps = zip(comprehensions, comprehensions[1:])
growths = [x for x in list(enumerate(steps)) if x[1][0] != x[1][1]]

# print the results:
for growth in growths:
    print(growth)
</code></pre>

<p>Results (format is <code>(list length, (old total size, new total size))</code>):</p>

<pre><code>(0, (64, 96)) 
(4, (96, 128))
(8, (128, 192))
(16, (192, 264))
(25, (264, 344))
(35, (344, 432))
(46, (432, 528))
(58, (528, 640))
(72, (640, 768))
(88, (768, 912))
</code></pre>

<hr>

<p>The over-allocation is done for performance reasons allowing lists to grow without allocating more memory with every growth (better <a href="https://en.wikipedia.org/wiki/Amortized_analysis" rel="nofollow">amortized</a> performance).</p>

<p>A probable reason for the difference with using list comprehension, is that list comprehension can not deterministically calculate the size of the generated list, but <code>list()</code> can. This means comprehensions will continuously grow the list as it fills it using over-allocation until finally filling it.</p>

<p>It is possible that is will not grow the over-allocation buffer with unused allocated nodes once its done (in fact, in most cases it wont, that would defeat the over-allocation purpose).</p>

<p><code>list()</code>, however, can add some buffer no matter the list size since it knows the final list size in advance.</p>

<hr>

<p>Another backing evidence, also from the source, is that we see <a href="https://github.com/python/cpython/blob/3.5/Python/compile.c#L3374" rel="nofollow">list comprehensions invoking <code>LIST_APPEND</code></a>, which indicates usage of <code>list.resize</code>, which in turn indicates consuming the pre-allocation buffer without knowing how much of it will be filled. This is consistent with the behavior you're seeing.</p>

<hr>

<p>To conclude, <code>list()</code> will pre-allocate more nodes as a function of the list size</p>

<pre><code>&gt;&gt;&gt; sys.getsizeof(list([1,2,3]))
60
&gt;&gt;&gt; sys.getsizeof(list([1,2,3,4]))
64
</code></pre>

<p>List comprehension does not know the list size so it uses append operations as it grows, depleting the pre-allocation buffer:</p>

<pre><code># one item before filling pre-allocation buffer completely
&gt;&gt;&gt; sys.getsizeof([i for i in [1,2,3]]) 
52
# fills pre-allocation buffer completely
# note that size did not change, we still have buffered unused nodes
&gt;&gt;&gt; sys.getsizeof([i for i in [1,2,3,4]]) 
52
# grows pre-allocation buffer
&gt;&gt;&gt; sys.getsizeof([i for i in [1,2,3,4,5]])
68
</code></pre>

python list list-comprehension