2017-06-22 4 views
7

मैंने स्पष्टता और संगठनात्मक कारणों के लिए __init__ के अंदर कक्षा विशेषताओं को हमेशा घोषित करने का प्रयास किया है। हाल ही में, मैंने सीखा है कि इस अभ्यास के बाद सख्ती से अतिरिक्त गैर-सौंदर्य लाभ भी हैं PEP 412 को पायथन 3.3 के लिए जोड़ा जा रहा है। विशेष रूप से, यदि सभी गुण __init__ में परिभाषित किए गए हैं, तो ऑब्जेक्ट्स अपनी चाबियाँ और हैंश साझा करके स्थान को कम कर सकते हैं।क्या __init__ से बुलाए गए फ़ंक्शन में वैरिएबल घोषित करना अभी भी एक कुंजी-साझाकरण शब्दकोश का उपयोग करता है?

मेरा सवाल यह है कि ऑब्जेक्ट कुंजी-साझाकरण तब होता है जब __init__ द्वारा किसी फ़ंक्शन में विशेषता घोषित की जाती है?

यहाँ एक उदाहरण है:

class Dog: 
    def __init__(self): 
     self.height = 5 
     self.weight = 25 

class Cat: 
    def __init__(self): 
     self.set_shape() 

    def set_shape(self): 
     self.height = 2 
     self.weight = 10 

इस मामले में, Dog के सभी उदाहरणों कुंजी height और weight का हिस्सा होगा। Cat के उदाहरण भी height और weight (एक दूसरे के बीच, Dog के साथ नहीं) को साझा करें।

एक तरफ के रूप में, आप इसका परीक्षण कैसे करेंगे?

नोट ब्रैंडन रोड्स ने कहा कि में अपने Dictionary Even Mightier talk इस बारे में महत्वपूर्ण के बंटवारे:

एक एकल कुंजी जोड़ा जाता है कि चाबियों का प्रोटोटाइप सेट में नहीं है, आप ढीला कुंजी साझा करने

+1

सीधे आपके प्रश्न का उत्तर नहीं दे रहा है, लेकिन PyCharm में निरीक्षण हैं जो चेतावनी देते हैं कि क्या आप '__init__' के बाहर नए विशेषताओं को बनाते हैं। तो वे शायद, यह एक बुरा अभ्यास मानते हैं। – wim

उत्तर

6

ऑब्जेक्ट कुंजी-साझाकरण तब होता है जब __init__ द्वारा फ़ंक्शन में विशेषताएँ घोषित की जाती हैं?

हां, चाहे आप गुण कहां सेट करते हैं, यह देखते हुए कि प्रारंभिकता के बाद दोनों के पास एक ही कुंजी है, उदाहरण शब्दकोश शब्दकोश साझा-कुंजी शब्दकोश कार्यान्वयन का उपयोग करते हैं। प्रस्तुत किए गए दोनों मामलों में कम स्मृति पदचिह्न है।

आप उदाहरण शब्दकोश के आकार को पकड़ने के लिए sys.getsizeof का उपयोग करके इसका परीक्षण कर सकते हैं और उसके बाद इसे बनाए गए समान निर्देश के साथ इसकी तुलना कर सकते हैं। विभिन्न आकारों लौट dict.__sizeof__ के कार्यान्वयन इस के आधार पर भेदभाव करने के लिए:

# on 64bit version of Python 3.6.1 
print(sys.getsizeof(vars(c))) 
112 
print(getsizeof(dict(vars(c)))) 
240 

हां, पता लगाने के लिए, तुम सब करने की जरूरत है इन तुलना है।

आपके संपादन के लिए के रूप में:

"एक ही कुंजी जोड़ा जाता है कि चाबियों का प्रोटोटाइप सेट में नहीं है, आप ढीला कुंजी साझा"

सही है, इस में से एक है दो बातें मैंने (वर्तमान में) में पाया गया कि तोड़ साझा कुंजी उपयोग:

  1. उदाहरण dict में एक गैर-स्ट्रिंग कुंजी का उपयोग करना। यह केवल मूर्ख तरीके से किया जा सकता है।(आप इसे vars(inst).update का उपयोग करके कर सकते हैं)
  2. उसी वर्ग के दो उदाहरणों के शब्दकोशों की सामग्रियों की सामग्री विचलन, यह उदाहरण शब्दकोशों को बदलकर किया जा सकता है। (इसमें जोड़ा गया एक कुंजी कुंजी के प्रोटोटाइपिकल सेट में नहीं है)

    मुझे यह नहीं पता कि यह तब होता है जब एकल कुंजी जोड़ दी जाती है, यह एक कार्यान्वयन विवरण है जो बदल सकता है। Why is the __dict__ of instances so small in Python 3?

    ये दोनों बातें CPython बजाय एक 'सामान्य' शब्दकोश का उपयोग करने के कारण होगा: देखना एक क्यू & एक मैं यहाँ किया था इस पर संबंधित चर्चा के लिए: (परिशिष्ट देखना मार्टिन की टिप्पणी)

। यह, ज़ाहिर है, एक कार्यान्वयन विस्तार है जिस पर भरोसा नहीं किया जाना चाहिए। आप पाइथन और सीपीथॉन के भविष्य के संस्करणों के अन्य कार्यान्वयन में इसे पा सकते हैं या नहीं।

+0

आपको अभी भी '__init__' में विशेषता जोड़ने की ज़रूरत है, है ना? –

+1

@NathanielSaul अगर आप चाहते हैं। प्वाइंट है, इससे कोई फर्क नहीं पड़ता है जब तक कि आप उन्हें स्ट्रिंग कुंजियों तक जोड़ते हैं और दो उदाहरणों का शब्दकोश सामग्री में ज्यादा विचलित नहीं होता है, आपको साझा-कुंजी निर्देश मिलता है। –

+0

फॉलो अप के लिए धन्यवाद। लगता है जैसे प्रारंभिकता '__init__' में कहीं भी होती है, कुंजी साझाकरण कार्य करता है। लेकिन अगर यह '__init__' के बाद होता है, तो कुंजी-साझाकरण ब्रेक। –

5

मुझे लगता है कि आप पीईपी के निम्नलिखित पैराग्राफ (Split-Table dictionaries section में) की बात कर रहे:

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

तो एक शब्दकोश कुंजी साझा रहेगा, कोई बात नहीं क्या अतिरिक्त बना रहे हैं, से पहले एक दूसरे उदाहरण बनाया जा सकता है। __init__ में ऐसा करने से यह प्राप्त करने का सबसे तार्किक तरीका है।

यह का अर्थ यह नहीं है कि बाद में सेट किए गए गुण साझा नहीं किए जाते हैं; वे अभी भी उदाहरण के बीच साझा कर सकते हैं; जब तक आप किसी भी शब्दकोश को संयुक्त नहीं जोड़ते हैं। तो तुम एक दूसरे उदाहरण बनाने के बाद, कुंजी साझा किया जा रहा रोक अगर निम्न में से कोई होता है:

  • एक नई विशेषता का कारण बनता है शब्दकोश आकार दिया जा करने के लिए
  • एक नई विशेषता कोई स्ट्रिंग विशेषता नहीं है (शब्दकोशों अत्यधिक हैं आम ऑल-कुंजियों-स्ट्रिंग केस के लिए अनुकूलित)।
  • एक विशेषता एक अलग क्रम में डाली गई है; उदाहरण के लिए a.foo = None और b.bar = None, यहां b.bar में एक असंगत प्रविष्टि आदेश है, क्योंकि साझा शब्दकोश में foo पहले है।
  • एक विशेषता हटा दी गयी है। यह एक उदाहरण के लिए भी साझा करना मारता है। यदि आप साझा शब्दकोशों की परवाह करते हैं तो विशेषताओं को हटाएं।

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

इसका यह भी मतलब है कि सौंपने सेटिंग __init__ उपरोक्त परिदृश्य को प्रभावित करने के लिए नहीं जा रहा है से नामक एक सहायक विधि के लिए जिम्मेदार बताते हैं, उन विशेषताओं अभी भी करने से पहले एक दूसरे उदाहरण बनाई गई है स्थापित कर रहे हैं। सभी __init__ के बाद उस दूसरी विधि को वापस करने से पहले अभी तक वापस नहीं आ सकेंगे।

दूसरे शब्दों में, आपको अपनी विशेषताओं को सेट करने के बारे में बहुत चिंता नहीं करनी चाहिए। उन्हें __init__ विधि में सेट करने से आप परिदृश्यों को अधिक आसानी से संयोजित करने से बच सकते हैं, लेकिन किसी दूसरे उदाहरण से पहले कोई विशेषता सेट बनाई गई है साझा कुंजी का हिस्सा होने की गारंटी है।

इस परीक्षण के लिए कैसे करें: sys.getsizeof() function के साथ मेमोरी आकार को देखें; अगर एक बड़ी वस्तु में __dict__ मानचित्रण परिणामों की एक प्रतिलिपि बनाने, __dict__ तालिका साझा किया गया था:

import sys 

def shared(instance): 
    return sys.getsizeof(vars(instance)) < sys.getsizeof(dict(vars(instance))) 

एक त्वरित प्रदर्शन:

>>> class Foo: 
...  pass 
... 
>>> a, b = Foo(), Foo() # two instances 
>>> shared(a), shared(b) # they both share the keys 
(True, True) 
>>> a.bar = 'baz' # adding a single key 
>>> shared(a), shared(b) # no change, the keys are still shared! 
(True, True) 
>>> a.spam, a.ham, a.monty, a.eric = (
...  'eggs', 'eggs and spam', 'python', 
...  'idle') # more keys still 
>>> shared(a), shared(b) # no change, the keys are still shared! 
(True, True) 
>>> a.holy, a.bunny, a.life = (
...  'grail', 'of caerbannog', 
...  'of brian') # more keys, resize time 
>>> shared(a), shared(b) # oops, we killed it 
(False, False) 

केवल जब सीमा पर पहुँच गया था (एक खाली के लिए 8 अतिरिक्त स्लॉट के साथ शब्दकोश, जब आप 6 वां कुंजी जोड़ते हैं तो आकार बदलता है), क्या शब्दकोश साझा संपत्ति को ढीला करता था।

जब वे लगभग 2/3 पूर्ण होते हैं तो शब्दकोश का आकार बदलता है, और एक आकार आमतौर पर तालिका आकार को दोगुना करता है। तो अगला आकार तब होगा जब 11 वीं कुंजी जोड़ दी जाएगी, फिर 22, फिर 43, आदि। तो बड़े इंस्टेंस डिक्शनरी के लिए, आपके पास बहुत अधिक सांस लेने का कमरा है।

+0

@NathanielSaul: मैंने अपना जवाब स्पष्ट कर दिया है।मुद्दा यह है कि 'कट ऑफ पॉइंट' एक दूसरा उदाहरण बना रहा है, जिस बिंदु पर साझा की गई तालिका में पहले से मौजूद सभी कुंजियां 'कम पानी चिह्न' के रूप में देखी जाती हैं। इसमें जोड़े गए किसी भी नई कुंजी को साझा शब्दकोश को नियमित (संयुक्त) शब्दकोश में बदल दिया जा सकता है, लेकिन केवल तभी बदला जा सकता है। –

+0

इस तरह के 'कट ऑफ पॉइंट' के बारे में सोचकर यह बहुत स्पष्ट हो जाता है। धन्यवाद! –

+0

@ नथनीलसेल: मैंने कोडबेस के गहरे गोताखोर किए, मुझे लगता है कि अब सभी एज-केस शामिल हैं। –

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