python - सूची() सूची बोध की तुलना में अधिक स्मृति का उपयोग करता है




list list-comprehension (2)

इसलिए मैं list वस्तुओं के साथ खेल रहा था और थोड़ी अजीब बात यह पाई गई कि यदि list के साथ list() बनाई गई है list() तो सूची संकलन की तुलना में यह अधिक स्मृति का उपयोग करता है? मैं पायथन 3.5.2 का उपयोग कर रहा हूं

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

docs :

सूचियों का निर्माण कई तरीकों से किया जा सकता है:

  • खाली सूची को दर्शाने के लिए वर्ग कोष्ठक की एक जोड़ी का उपयोग करना: []
  • वर्ग कोष्ठक का उपयोग करना, अल्पविराम से वस्तुओं को अलग करना: [a] , [a, b, c]
  • सूची समझ का उपयोग करना: [x for x in iterable]
  • टाइप कंस्ट्रक्टर का उपयोग करना: list() या list(iterable)

लेकिन ऐसा लगता है कि list() का उपयोग करके यह अधिक मेमोरी का उपयोग करता है।

और जैसे-जैसे list बड़ी होती है, अंतर बढ़ता जाता है।

ऐसा क्यूँ होता है?

अद्यतन # 1

पायथन 3.6.0b2 के साथ टेस्ट:

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.
>>> import sys
>>> sys.getsizeof(list(range(100)))
1008
>>> sys.getsizeof([i for i in range(100)])
912

अद्यतन # 2

पायथन 2.7.12 के साथ टेस्ट:

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.
>>> import sys
>>> sys.getsizeof(list(xrange(100)))
1016
>>> sys.getsizeof([i for i in xrange(100)])
920

उस भयानक अजगर को समझने में मेरी मदद करने के लिए सभी को धन्यवाद।

मैं यह सवाल नहीं करना चाहता हूं कि बड़े पैमाने पर (इसीलिए मैं उत्तर पोस्ट कर रहा हूं), बस अपने विचारों को दिखाना और साझा करना चाहता हूं।

जैसा कि ने सही उल्लेख किया: "सूची () निश्चित रूप से सूची आकार निर्धारित करता है"। आप इसे उस ग्राफ से देख सकते हैं।

जब आप सूची append या उपयोग करते हैं तो आपके पास हमेशा कुछ प्रकार की सीमाएँ होती हैं जो किसी बिंदु पर पहुंचने पर विस्तारित होती हैं। और list() आपके पास लगभग समान सीमाएं हैं, लेकिन वे तैर रहे हैं।

अद्यतन करें

तो , tavo , @SvenFestersen को धन्यवाद

योग करने के लिए: list() उपदेश मेमोरी मेमोरी के आकार पर निर्भर करती है, सूची की समझ ऐसा नहीं कर सकती है (यह आवश्यकता होने पर अधिक मेमोरी का अनुरोध करती है, जैसे .append() ) इसीलिए list() अधिक मेमोरी स्टोर करें।

एक और ग्राफ, वह शो list() मेमोरी का प्रचार करता है। इसलिए ग्रीन लाइन list(range(830)) तत्व को जोड़कर और थोड़ी देर के लिए स्मृति नहीं बदलती है।

अद्यतन २

जैसा कि @Barmar नीचे टिप्पणी में उल्लेख किया है, list() बोध की तुलना में मुझे तेज होना चाहिए, इसलिए मैंने 4**0 से 4**10 तक की लंबाई के लिए number=1000 साथ timeit() चलाया और परिणाम हैं


मुझे लगता है कि आप ओवर-आवंटन पैटर्न देख रहे हैं यह स्रोत से एक नमूना है :

/* 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 >> 3) + (newsize < 9 ? 3 : 6);

0-88 लंबाई के सूची बोध के आकारों को मुद्रित करना आप पैटर्न के मैच देख सकते हैं:

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

परिणाम (प्रारूप है (list length, (old total size, new total size)) ):

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

ओवर-आबंटन प्रदर्शन कारणों से किया जाता है, जिससे सूचियों को हर विकास (बेहतर amortized प्रदर्शन) के साथ अधिक मेमोरी आवंटित किए बिना बढ़ने की अनुमति मिलती है।

सूची समझ का उपयोग करने के साथ अंतर का एक संभावित कारण, यह है कि सूची की समझ उत्पन्न सूची के आकार की गणना नहीं कर सकती है, लेकिन list() कर सकती है। इसका मतलब यह है कि समझ लगातार बढ़ेगी क्योंकि यह अंतिम रूप से भरने तक इसे ओवर-आवंटन का उपयोग करके भरता है।

यह संभव है कि एक बार किए गए अप्रयुक्त नोड्स के साथ ओवर-आवंटन बफर नहीं बढ़ेगा (वास्तव में, ज्यादातर मामलों में यह अभ्यस्त है, जो ओवर-आवंटन उद्देश्य को हरा देगा)।

list() , हालांकि, कुछ बफर को सूची के आकार से जोड़ सकते हैं क्योंकि यह अंतिम सूची आकार पहले से जानता है।

स्रोत से भी एक और बैकिंग साक्ष्य, यह है कि हम LIST_APPEND करते LIST_APPEND सूची LIST_APPEND को LIST_APPEND , जो सूची के उपयोग को इंगित करता है। list.resize , जो बदले में पूर्व आवंटन बफर का उपभोग किए बिना इंगित करता है कि यह कितना भरा जाएगा। यह आपके द्वारा देखे जा रहे व्यवहार के अनुरूप है।

निष्कर्ष निकालने के लिए, सूची आकार के एक फ़ंक्शन के रूप में list() को अधिक नोड आवंटित करेगा

>>> sys.getsizeof(list([1,2,3]))
60
>>> sys.getsizeof(list([1,2,3,4]))
64

सूची की समझ सूची के आकार को नहीं जानती है, इसलिए यह अपग्रेड ऑपरेशन का उपयोग करता है क्योंकि यह बढ़ता है, पूर्व-आवंटन बफर को कम करता है:

# one item before filling pre-allocation buffer completely
>>> 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
>>> sys.getsizeof([i for i in [1,2,3,4]]) 
52
# grows pre-allocation buffer
>>> sys.getsizeof([i for i in [1,2,3,4,5]])
68







python-internals