2010-01-23 18 views
29

दो ही वर्ण, एक == ख के साथ अजगर तार, स्मृति, आईडी (क) == आईडी (ख), साझा कर सकते हैं या दो बार स्मृति में हो सकता है, आईडी (क)! = आईडी (ख)। पाइथन समान स्ट्रिंग के लिए नई मेमोरी आवंटित करता है?

ab = "ab" 
print id(ab), id("a"+"b") 

यहाँ अजगर स्वीकार करता है कि नव निर्मित "एक" + "ख" एक ही "ab" पहले से ही स्मृति में के रूप में है की कोशिश करो - बुरा नहीं।

अब राज्य के नाम के एक एन-लंबी सूची पर विचार [ "एरिजोना", "अलास्का", "अलास्का", "कैलिफोर्निया" ...] (एन ~ मेरे मामले में 500000)।
मैं 50 अलग-अलग आईडी() s ⇒ प्रत्येक स्ट्रिंग "एरिजोना" देखता हूं ... केवल एक बार संग्रहीत किया जाता है, ठीक है।
लेकिन डिस्क पर सूची लिखें और इसे फिर से पढ़ें: "समान" सूची में अब एन अलग आईडी() एस है, और अधिक मेमोरी है, नीचे देखें।

कैसे आते हैं - क्या कोई पाइथन स्ट्रिंग मेमोरी आवंटन समझा सकता है?

""" when does Python allocate new memory for identical strings ? 
    ab = "ab" 
    print id(ab), id("a"+"b") # same ! 
    list of N names from 50 states: 50 ids, mem ~ 4N + 50S, each string once 
    but list > file > mem again: N ids, mem ~ N * (4 + S) 
""" 

from __future__ import division 
from collections import defaultdict 
from copy import copy 
import cPickle 
import random 
import sys 

states = dict(
AL = "Alabama", 
AK = "Alaska", 
AZ = "Arizona", 
AR = "Arkansas", 
CA = "California", 
CO = "Colorado", 
CT = "Connecticut", 
DE = "Delaware", 
FL = "Florida", 
GA = "Georgia", 
) 

def nid(alist): 
    """ nr distinct ids """ 
    return "%d ids %d pickle len" % (
     len(set(map(id, alist))), 
     len(cPickle.dumps(alist, 0))) # rough est ? 
# cf http://stackoverflow.com/questions/2117255/python-deep-getsizeof-list-with-contents 

N = 10000 
exec("\n".join(sys.argv[1:])) # var=val ... 
random.seed(1) 

    # big list of random names of states -- 
names = [] 
for j in xrange(N): 
    name = copy(random.choice(states.values())) 
    names.append(name) 
print "%d strings in mem: %s" % (N, nid(names)) # 10 ids, even with copy() 

    # list to a file, back again -- each string is allocated anew 
joinsplit = "\n".join(names).split() # same as > file > mem again 
assert joinsplit == names 
print "%d strings from a file: %s" % (N, nid(joinsplit)) 

# 10000 strings in mem: 10 ids 42149 pickle len 
# 10000 strings from a file: 10000 ids 188080 pickle len 
# Python 2.6.4 mac ppc 

जोड़ा गया 25jan:
पायथन स्मृति में तार के दो प्रकार के (या किसी भी कार्यक्रम के) होते हैं:

  • Ustrings अद्वितीय तार का एक Ucache में, इन स्मृति बचाने के लिए, और एक बनाने के = = बी तेज़ अगर दोनों Ucache
  • शुतुरमुर्ग, अन्य हैं, जो किसी भी समय संग्रहीत किए जा सकते हैं।

intern(astring) यूकेचे (एलेक्स +1) में अस्थिर रखता है; इसके अलावा हम कुछ भी नहीं जानते कि पाइथन ओस्ट्रिंग को यूकेचे में कैसे ले जाता है - "ए" के बाद "ए" + "बी" कैसे मिलता है? ("फाइलों से स्ट्रिंग्स" व्यर्थ है - जानने का कोई तरीका नहीं है।)
संक्षेप में, यूकेच (कई हो सकते हैं) धुंधले रहेंगे।

एक ऐतिहासिक फुटनोट: SPITBOL सभी तारों को विशिष्टता सीए। 1970

उत्तर

36

प्रत्येक कार्यान्वयन अजगर भाषा का अपरिवर्तनीय वस्तुओं (जैसे तारों के रूप में) के आवंटन में अपने स्वयं के तालमेल बनाने के लिए नि: शुल्क है - या तो एक नया एक बनाने या किसी मौजूदा बराबर एक खोजने और इसे करने के लिए एक और संदर्भ का उपयोग करते हुए, भाषा के दृष्टिकोण से ठीक है।अभ्यास में, ज़ाहिर है, वास्तविक दुनिया के कार्यान्वयन पर उचित समझौता हुआ है: ऐसी वस्तु का पता लगाने के दौरान एक उपयुक्त मौजूदा वस्तु का एक और संदर्भ सस्ता और आसान है, अगर एक उपयुक्त मौजूदा (जो हो सकता है या अस्तित्व में नहीं हो सकता है) ऐसा लगता है कि यह संभावित रूप से लंबे समय तक खोज कर सकता है।

तो, उदाहरण के लिए, एक ही फ़ंक्शन के भीतर एक ही स्ट्रिंग अक्षर की कई घटनाएं (सभी कार्यान्वयन में मुझे पता है) में "उसी ऑब्जेक्ट के नए संदर्भ" रणनीति का उपयोग करें, क्योंकि उस फ़ंक्शन के स्थिरांक-पूल का निर्माण करते समय यह सुंदर है डुप्लिकेट से बचने के लिए तेज़ और आसान; लेकिन अलग-अलग फ़ंक्शंस अलग-अलग समय-समय पर उपभोग करने वाले कार्य हो सकते हैं, इसलिए असली दुनिया के कार्यान्वयन या तो ऐसा नहीं करते हैं, या केवल कुछ मामलों में कुछ हद तक पहचानने वाले सबसेट में ऐसा करते हैं जहां कोई उचित के लिए आशा कर सकता है संकलन समय का व्यापार (समान मौजूदा स्थिरांक की खोज करके धीमा) स्मृति की खपत बनाम (अगर स्थिरांक की नई प्रतियां बनायी जाती हैं तो बढ़ी)।

मुझे पायथन (या उस मामले के लिए जावा जैसी स्थिर तारों वाली अन्य भाषाओं के लिए) के किसी भी कार्यान्वयन के बारे में पता नहीं है जो डेटा पढ़ने के दौरान संभावित डुप्लीकेट (एकाधिक संदर्भों के माध्यम से एक ऑब्जेक्ट का पुन: उपयोग करने) की पहचान करने में परेशानी लेता है एक फ़ाइल से - यह सिर्फ एक आशाजनक व्यापारिक प्रतीत नहीं होता है (और यहां आप रनटाइम का भुगतान नहीं करेंगे, संकलित समय संकलित नहीं करते हैं, इसलिए ट्रेडऑफ भी कम आकर्षक है)। बेशक, यदि आप जानते हैं (आवेदन स्तर के विचारों के लिए धन्यवाद) कि ऐसी अपरिवर्तनीय वस्तुएं बड़ी हैं और कई डुप्लिकेशन्स के लिए काफी प्रवण हैं, तो आप अपने स्वयं के "स्थिरांक-पूल" रणनीति को आसानी से कार्यान्वित कर सकते हैं (intern तारों के लिए इसे करने में आपकी सहायता कर सकता है, लेकिन अपने आप को रोल करना मुश्किल नहीं है, उदाहरण के लिए, अपरिवर्तनीय वस्तुओं, विशाल लंबे पूर्णांक, और आगे के साथ tuples)।

+0

क्या मेरे उत्तर में मूल्य का कुछ भी है जो आपको नहीं लगता कि आपके भीतर शामिल है? यदि नहीं, तो मैं अपना जवाब हटा दूंगा। यदि वहां है, तो क्या आप इसे अपने में संपादित करना चाहते हैं और * फिर * मैं अपना जवाब हटा दूंगा? 'Intern' का उल्लेख करने के लिए –

+0

+1। मैं पूरी तरह से भूल गया था कि यह कार्य अस्तित्व में था। एन में "joinsplit = [intern (n) का उपयोग करके \ n"। Join (names) .split()] 'नौकरी की और मेरे मैकबुक पर 4,374,528 से 3,190,783 तक मेमोरी उपयोग कम किया। –

+0

@ जॉन, मुझे लगता है कि पाइथन पर एक विशेष "अंदरूनी के परिप्रेक्ष्य" के बिना एक अनुभवी प्रोग्रामर से आपका दो दृष्टिकोण (मेरा "अंदरूनी परिप्रेक्ष्य" से मेरा) मानना ​​महत्वपूर्ण है - यह सुनिश्चित नहीं है कि यह प्राप्त करने का एक इष्टतम तरीका है एक ही जवाब में एक ही "त्रिकोण"! –

16

मैं दृढ़ता से संदेह है कि अजगर कई अन्य भाषाओं की तरह यहाँ बर्ताव कर रही है - पहचानने स्ट्रिंग अपने स्रोत कोड भीतर स्थिरांक और उन लोगों के लिए एक आम तालिका का उपयोग कर, लेकिन नहीं जब तार गतिशील बनाने में एक ही नियमों को लागू करने। यह समझ में आता है क्योंकि आपके स्रोत कोड में केवल तारों का एक सीमित सेट होगा (हालांकि पायथन आपको गतिशील रूप से कोड का मूल्यांकन करने देता है) जबकि यह अधिक संभावना है कि आप अपने कार्यक्रम के दौरान बड़ी संख्या में तार बनायेंगे ।

इस प्रक्रिया को आम तौर पर इंटर्निंग कहा जाता है - और वास्तव में this page के रूप में इसे पायथन में भी इंटर्निंग कहा जाता है।

+0

कोई विचार तो आईडी ("ab") == आईडी ("a" + "b") क्यों? क्या आप इस बात से सहमत होंगे कि हम नहीं जानते कि पाइथन यूकेच कैसे चलाता है? – denis

+3

पूर्णता के लिए: अभिव्यक्ति '" एक "+" बी "' स्थिर रूप से अभिव्यक्ति '' ab '' में बदल दी गई है, जिसे तब एक ही स्ट्रिंग के रूप में पाया जाता है। यह सब संकलन समय पर होता है। –

2
x = 42 
y = 42 
x == y #True 
x is y #True 

इस बातचीत में, एक्स और वाई == (एक ही मूल्य), लेकिन (एक ही वस्तु) है क्योंकि हम दो अलग अलग शाब्दिक भाव भाग गया होना चाहिए। क्योंकि छोटे पूर्णांक और तार कैश किए गए हैं और पुन: उपयोग, हालांकि, हमें बताता है कि वे एक ही ऑब्जेक्ट को संदर्भित करते हैं।

वास्तव में, यदि आप वास्तव में हुड के नीचे देखना चाहते हैं, तो आप हमेशा अजगर एक वस्तु को मानक sys मॉड्यूल वस्तु के संदर्भ रिटर्न में getrefcount फंक्शन का उपयोग करके पूछ सकते हैं कि कितने संदर्भ वहाँ हैं गिनती। यह व्यवहार कई तरीकों में से एक को दर्शाता है जिस तरह से पाइथन निष्पादन गति के लिए अपने मॉडल को अनुकूलित करता है।

Learning Python

10

एक ओर ध्यान दें: यह अजगर में वस्तुओं के जीवन पता करने के लिए बहुत महत्वपूर्ण है। नोट निम्नलिखित सत्र:

Python 2.6.4 (r264:75706, Dec 26 2009, 01:03:10) 
[GCC 4.3.4] on linux2 
Type "help", "copyright", "credits" or "license" for more information. 
>>> a="a" 
>>> b="b" 
>>> print id(a+b), id(b+a) 
134898720 134898720 
>>> print (a+b) is (b+a) 
False 

आपकी सोच है कि दो अलग भाव की आईडी मुद्रण और ध्यान देने योग्य बात से "वे फलस्वरूप दो भाव बराबर/बराबर/ही होना चाहिए बराबर हैं" दोषपूर्ण है। आउटपुट की एक पंक्ति में जरूरी नहीं है कि इसकी सभी सामग्रियां बनाई गई हों और/या समय में एक ही पल में सह-अस्तित्व में हों।

यदि आप जानना चाहते हैं कि दो वस्तुएं एक ही वस्तु हैं, तो सीधे पायथन से पूछें (is ऑपरेटर का उपयोग करके)।

+5

यहां क्या हो रहा है के बारे में कुछ स्पष्टीकरण: 'प्रिंट आईडी (ए + बी), आईडी (बी + ए) 'लाइन पहले" ए "और" बी "को एक नई आवंटित स्ट्रिंग" एबी "में जोड़ती है, फिर 'id' को पास करता है, फिर इसे तब तक हटा देता है जब इसकी आवश्यकता नहीं होती है। फिर "बीए" को उसी तरह आवंटित किया जाता है, और स्मृति में उसी स्थान पर आवंटित किया जाता है (सीपीथन में ऐसा करने की आदत है)। "बीए" को फिर 'आईडी' में भेज दिया जाता है, जो एक ही परिणाम देता है। अगली पंक्ति के साथ, हालांकि, "ab" और "ba" दोनों को 'is' ऑपरेटर को पास करने के लिए चारों ओर रखा जाता है, इसलिए उन्हें अलग-अलग स्थितियों पर आवंटित किया जाता है। – javawizard

संबंधित मुद्दे