पायथन की सूची कैसे लागू की जाती है?


183

क्या यह एक लिंक्ड सूची, एक सरणी है? मैंने चारों ओर खोज की और केवल लोगों को अनुमान लगाया। स्रोत कोड को देखने के लिए मेरा सी ज्ञान पर्याप्त नहीं है।

जवाबों:


58

यह एक गतिशील सरणी है । व्यावहारिक प्रमाण: अनुक्रमण (बेहद कम अंतरों के साथ निश्चित रूप से (0.0013 µsecs!)) सूचकांक की परवाह किए बिना एक ही समय लेता है:

...>python -m timeit --setup="x = [None]*1000" "x[500]"
10000000 loops, best of 3: 0.0579 usec per loop

...>python -m timeit --setup="x = [None]*1000" "x[0]"
10000000 loops, best of 3: 0.0566 usec per loop

मुझे आश्चर्य होगा कि अगर आयरनपाइथन या जाइथन ने लिस्टेड लिस्ट का इस्तेमाल किया - तो वे इस धारणा पर बनी कई व्यापक रूप से इस्तेमाल की जाने वाली लाइब्रेरियों के प्रदर्शन को बर्बाद कर देंगे कि लिस्ट डायनेमिक अरे हैं।


1
@ राल्फ: मुझे पता है कि मेरा सीपीयू (सबसे अधिक हार्डवेयर भी, उस मामले के लिए) पुराना है और कुत्ता धीमा है - उज्ज्वल पक्ष पर, मैं यह मान सकता हूं कि कोड जो मेरे लिए काफी तेजी से चलता है वह सभी उपयोगकर्ताओं के लिए पर्याप्त तेज है: डी

88
@ डायलेन: -1 आपका "व्यावहारिक प्रमाण" एक बकवास है, जैसा कि 6 अपवोट्स हैं। लगभग 98% समय ऐसा करने में लिया जाता है x=[None]*1000, जिससे किसी भी संभावित सूची के उपयोग के अंतर को नापा जा सकता है। आपको इनिशियलाइज़ेशन को अलग करने की आवश्यकता है:-s "x=[None]*100" "x[0]"
जॉन मैकिन

26
दिखाता है कि यह किसी लिंक की गई सूची का भोली कार्यान्वयन नहीं है। निश्चित रूप से नहीं दिखाता है कि यह एक सरणी है।
बजे माइकल मिओर

6
आप इसके बारे में यहां पढ़ सकते हैं: docs.python.org/2/faq/design.html#how-are-lists-implemented
CCoder

3
केवल लिंक की गई सूची और सरणी से कहीं अधिक संरचनाएं हैं, समय उनके बीच निर्णय लेने के लिए कोई व्यावहारिक उपयोग नहीं है।
रॉस हेम्सले

236

सी कोड वास्तव में बहुत सरल है। एक मैक्रो का विस्तार करना और कुछ अप्रासंगिक टिप्पणियों को दूर करना, मूल संरचना में है listobject.h, जो एक सूची को इस प्रकार परिभाषित करता है:

typedef struct {
    PyObject_HEAD
    Py_ssize_t ob_size;

    /* Vector of pointers to list elements.  list[0] is ob_item[0], etc. */
    PyObject **ob_item;

    /* ob_item contains space for 'allocated' elements.  The number
     * currently in use is ob_size.
     * Invariants:
     *     0 <= ob_size <= allocated
     *     len(list) == ob_size
     *     ob_item == NULL implies ob_size == allocated == 0
     */
    Py_ssize_t allocated;
} PyListObject;

PyObject_HEADएक संदर्भ संख्या और एक प्रकार का पहचानकर्ता होता है। तो, यह एक वेक्टर / सरणी है जो समग्र है। पूर्ण होने पर ऐसी सारणी को आकार देने के लिए कोड listobject.c। यह वास्तव में सरणी को दोगुना नहीं करता है, लेकिन आवंटित करके बढ़ता है

new_allocated = (newsize >> 3) + (newsize < 9 ? 3 : 6);
new_allocated += newsize;

क्षमता हर बार, जहां newsize अनुरोधित आकार है (आवश्यक नहीं है allocated + 1क्योंकि आप extendतत्वों की एक मनमानी संख्या के बजाय append'उन्हें एक से एक कर सकते हैं)।

अजगर अक्सर पूछे जाने वाले प्रश्न भी देखें ।


6
इसलिए, जब अजगर सूचियों पर पुनरावृत्ति करता है तो यह लिंक की गई सूचियों जितना धीमा होता है, क्योंकि हर प्रविष्टि सिर्फ एक संकेतक है, इसलिए हर तत्व सबसे अधिक संभावना कैश मिस का कारण होगा।
Kr0e

9
@ Kr0e: नहीं अगर बाद के तत्व वास्तव में समान वस्तु हैं :) लेकिन अगर आपको छोटे / कैश-फ्रेंडली डेटा संरचनाओं की आवश्यकता है, तो arrayमॉड्यूल या न्यूमपी को प्राथमिकता दी जाएगी।
फ्रेड फू

@ Kr0e मैं यह नहीं कहूंगा कि सूची से संबंधित लिंकिंग सूचियों की तुलना में धीमी है, लेकिन यह है कि लिंक की गई सूचियों के मूल्यों पर पुनरावृत्ति एक जुड़ी हुई सूची के रूप में धीमी है, जो कि फ्रेड ने उल्लेख किया है। उदाहरण के लिए, किसी सूची को किसी दूसरे पर कॉपी करने के लिए पुनरावृत्ति एक लिंक की गई सूची से अधिक तेज़ होनी चाहिए।
गनेया डैन आंद्रेई

35

CPython में, सूचियाँ पॉइंटर्स के सरणियाँ हैं। अजगर के अन्य कार्यान्वयन उन्हें अलग-अलग तरीकों से संग्रहीत करना चुन सकते हैं।


32

यह कार्यान्वयन पर निर्भर है, लेकिन IIRC:

  • CPython पॉइंटर्स की एक सरणी का उपयोग करता है
  • Jython एक का उपयोग करता है ArrayList
  • IronPython जाहिरा तौर पर एक सरणी का उपयोग करता है। आप पता लगाने के लिए सोर्स कोड ब्राउज़ कर सकते हैं।

इस प्रकार इन सभी में O (1) यादृच्छिक अभिगम होता है।


1
एक अजगर दुभाषिया के रूप में कार्यान्वयन पर निर्भर करता है जो सूचियों को लिंक की गई सूचियों के रूप में लागू करता है क्या अजगर भाषा का एक वैध कार्यान्वयन होगा? दूसरे शब्दों में: O (1) सूचियों में रैंडम एक्सेस की गारंटी नहीं है? क्या कार्यान्वयन विवरण पर भरोसा किए बिना कुशल कोड लिखना असंभव है?
sepp2k

2
@ मुझे लगता है कि पायथन में सूचियों को केवल संग्रह का आदेश दिया गया है; उक्त कार्यान्वयन के कार्यान्वयन और / या प्रदर्शन आवश्यकताओं को स्पष्ट रूप से नहीं कहा गया है
NullUserException

6
@ sppe2k: चूंकि पायथन के पास वास्तव में एक मानक या औपचारिक विनिर्देश नहीं है (हालाँकि कुछ दस्तावेज़ों के कुछ टुकड़े हैं जो कहते हैं कि "... गारंटी है ..."), आप "इस" में 100% सुनिश्चित नहीं हो सकते कागज के कुछ टुकड़े की गारंटी है "। लेकिन चूंकि O(1)सूची अनुक्रमण एक बहुत ही सामान्य और मान्य धारणा है, कोई भी कार्यान्वयन इसे तोड़ने की हिम्मत नहीं करेगा।

@Paul यह बताता है कि सूचियों के अंतर्निहित कार्यान्वयन को कैसे किया जाना चाहिए।
NullUserException

यह सिर्फ चीजों के बड़े ओ चलने के समय को निर्दिष्ट करने के लिए नहीं होता है। भाषा वाक्यविन्यास विनिर्देश कार्यान्वयन के विवरण के रूप में एक ही बात का मतलब जरूरी नहीं है, यह बस अक्सर ऐसा होता है।
पॉल मैकमिलन

26

मैं लॉरेंट लूस के लेख "पायथन सूची कार्यान्वयन" का सुझाव दूंगा । मेरे लिए वास्तव में उपयोगी था क्योंकि लेखक बताता है कि कैसे सीपीथॉन में सूची को लागू किया गया है और इस उद्देश्य के लिए उत्कृष्ट आरेख का उपयोग करता है।

सूची ऑब्जेक्ट सी संरचना

CPython में एक सूची ऑब्जेक्ट को निम्न C संरचना द्वारा दर्शाया गया है। ob_itemसूची तत्वों को संकेत की एक सूची है। आबंटित मेमोरी में आवंटित स्लॉट की संख्या है।

typedef struct {
    PyObject_VAR_HEAD
    PyObject **ob_item;
    Py_ssize_t allocated;
} PyListObject;

आवंटित स्लॉट और सूची के आकार के बीच अंतर को नोटिस करना महत्वपूर्ण है। एक सूची के आकार के समान ही है len(l)। आवंटित स्लॉट की संख्या वह है जो मेमोरी में आवंटित की गई है। अक्सर, आप देखेंगे कि आवंटित आकार से अधिक हो सकता है। यह reallocहर बार नए तत्वों को सूची में संलग्न करने की आवश्यकता से बचने के लिए है।

...

संलग्न

हम सूची में एक पूर्णांक संलग्न करते हैं l.append(1):। क्या होता है?
यहां छवि विवरण दर्ज करें

हम एक और तत्व जोड़कर जारी रखने के लिए: l.append(2)list_resizen + 1 = 2 के साथ कहा जाता है, लेकिन क्योंकि आवंटित आकार 4 है, इसलिए अधिक मेमोरी आवंटित करने की आवश्यकता नहीं है। जब हम 2 अधिक पूर्णांकों जोड़ने एक ही बात होता है: l.append(3), l.append(4)। निम्नलिखित आरेख से पता चलता है कि हमारे पास अब तक क्या है।

यहां छवि विवरण दर्ज करें

...

सम्मिलित करें

आइए स्थिति 1 पर एक नया पूर्णांक (5) डालें l.insert(1,5)और आंतरिक रूप से जो होता है उसे देखें।यहां छवि विवरण दर्ज करें

...

पॉप

जब आप पिछले तत्व पॉप: l.pop(), listpop()कहा जाता है। list_resizeअंदर बुलाया जाता है listpop()और यदि नया आकार आवंटित आकार के आधे से कम है तो सूची सिकुड़ जाती है।यहां छवि विवरण दर्ज करें

आप उस स्लॉट 4 को अभी भी पूर्णांक को इंगित कर सकते हैं लेकिन महत्वपूर्ण बात सूची का आकार है जो अब 4 है। चलो एक और तत्व पॉप करते हैं। में list_resize(), आकार - 1 = 4 - 1 = 3 आवंटित स्लॉट के आधे से कम है इसलिए सूची 6 स्लॉट में सिकुड़ गई है और सूची का नया आकार अब 3 है।

आप उस स्लॉट 3 और 4 को अभी भी कुछ पूर्णांकों की ओर इंगित कर सकते हैं लेकिन महत्वपूर्ण बात सूची का आकार है जो अब 3 है।यहां छवि विवरण दर्ज करें

...

निकालें पायथन सूची ऑब्जेक्ट में किसी विशिष्ट तत्व को निकालने की एक विधि है l.remove(5):।यहां छवि विवरण दर्ज करें


धन्यवाद, मैं सूची के लिंक भाग को अब और अधिक समझता हूं । पायथन सूची एक है aggregation, नहीं composition। काश रचना की भी एक सूची होती।
शुवा

22

प्रलेखन के अनुसार ,

पायथन की सूचियाँ वास्तव में परिवर्तनशील-लंबाई की सरणियाँ हैं, लिस्प-शैली से जुड़ी सूचियाँ नहीं।


5

जैसा कि दूसरों ने ऊपर कहा है, सूचियों (जब सराहना बड़ी) को अंतरिक्ष की एक निश्चित राशि आवंटित करके कार्यान्वित किया जाता है, और, अगर उस स्थान को भरना चाहिए, तो बड़ी मात्रा में स्थान आवंटित करना और तत्वों की नकल करना।

यह समझने के लिए कि विधि O (1) परिशोधन के बिना सामान्यीकृत क्यों है, मान लें कि हमने एक = 2 ​​^ n तत्व डाला है, और हमें अब अपनी तालिका 2 ^ (n + 1) आकार से दोगुनी करनी होगी। इसका मतलब है कि हम वर्तमान में 2 ^ (n + 1) ऑपरेशन कर रहे हैं। अंतिम प्रतिलिपि, हमने 2 ^ एन ऑपरेशन किए। इससे पहले हमने 2 ^ (n-1) किया था ... सभी तरह से नीचे 8,4,2,1। अब, अगर हम इन्हें जोड़ते हैं, तो हमें 1 + 2 + 4 + 8 + ... + 2 ^ (n + 1) = 2 ^ (n + 2) - 1 <4 * 2 ^ n = O (2 ^) मिलता है n) = O (a) कुल सम्मिलन (यानी O (1) परिशोधन समय)। इसके अलावा, यह ध्यान दिया जाना चाहिए कि यदि तालिका हटाए जाने की अनुमति देती है तो तालिका को अलग कारक (जैसे 3x) पर सिकुड़ना पड़ता है


जहां तक ​​मैं समझता हूं, पुराने तत्वों की नकल नहीं है। अधिक स्थान आवंटित किया गया है, लेकिन नई जगह पहले से ही उपयोग किए जा रहे स्थान के साथ सन्निहित नहीं है, और केवल नए तत्व (एस) सम्मिलित करने के लिए नए स्थान पर कॉपी किए गए हैं। अगर मैं गलत हूं कृपया मुझे सही।
तुषार वाजिरानी

1

पायथन में एक सूची एक सरणी की तरह कुछ है, जहां आप कई मान संग्रहीत कर सकते हैं। सूची परिवर्तनशील है इसका मतलब है कि आप इसे बदल सकते हैं। अधिक महत्वपूर्ण बात जो आपको पता होनी चाहिए, जब हम एक सूची बनाते हैं, तो पायथन स्वचालित रूप से उस सूची चर के लिए एक reference_id बनाता है। यदि आप इसे बदलकर दूसरों को बदलते हैं तो मुख्य सूची बदल जाएगी। आइए एक उदाहरण के साथ प्रयास करें:

list_one = [1,2,3,4]

my_list = list_one

#my_list: [1,2,3,4]

my_list.append("new")

#my_list: [1,2,3,4,'new']
#list_one: [1,2,3,4,'new']

हम संलग्न हैं, my_listलेकिन हमारी मुख्य सूची बदल गई है। इसका मतलब यह है कि सूची की नकल के रूप में असाइन नहीं की गई सूची इसके संदर्भ के रूप में असाइन की गई है।


0

CPython सूची में गतिशील सरणी के रूप में कार्यान्वित किया जाता है, और इसलिए जब हम उस समय संलग्न करते हैं तो न केवल एक मैक्रो जोड़ा जाता है, बल्कि कुछ और स्थान आवंटित किया जाता है ताकि हर नए स्थान को जोड़ा न जाए।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.