python - उदाहरणों का__dict__ पायथन 3 में इतने छोटा आकार क्यों है?



python-3.x class (1)

संक्षेप में :

उदाहरण __dict__ ' __dict__ ' के साथ बनाए गए 'सामान्य' शब्दकोशों से अलग ढंग से लागू किए गए हैं या {} एक उदाहरण के शब्दकोश कुंजी और हंस साझा करते हैं और अलग-अलग भागों के लिए एक अलग सरणी रखें: मूल्य sys.getsizeof केवल उन मानों की गणना करता है जब उदाहरण के लिए आकार की गणना dict

थोड़ा और :

सीपीआईथोन में शब्दकोश, दो पायथनों में से एक में लागू किए गए पायथन 3.3 के रूप में हैं:

  • संयुक्त शब्दकोश : शब्दकोश के सभी मूल्यों को प्रत्येक प्रविष्टि के लिए कुंजी और हैश के साथ संग्रहीत किया जाता है ( me_value संरचना के me_value सदस्य ) जहाँ तक मुझे पता है, इस फ़ॉर्म का प्रयोग dict , {} और मॉड्यूल नामस्थान के साथ बनाए गए शब्दकोशों के लिए किया जाता है।
  • विभाजन तालिका : मूल्यों को एक सरणी में अलग से संग्रहित किया जाता है, जबकि चाबियां और हैश साझा की जाती हैं ( मूल्य में संग्रहीत है ma_values of PyDictObject )

इंस्टेंस शब्दकोश हमेशा एक स्प्लिट-टेबल फॉर्म (एक कुंजी-साझाकरण डिक्शनरी) में लागू होता है जो किसी दिए गए वर्ग के उदाहरणों को उनके __dict__ लिए कुंजी (और हैश) साझा करने की अनुमति देता है और केवल इसी मानों में भिन्न होता है।

यह सब पीईपी 412 में वर्णित है - कुंजी साझाकरण शब्दकोश स्प्लिट डिक्शनरी के लिए कार्यान्वयन पायथन 3.3 में उतरा, इसलिए 3 परिवार के पिछले संस्करणों के साथ ही पायथन 2.x में यह कार्यान्वयन नहीं है।

शब्दकोशों के लिए __sizeof__ का कार्यान्वयन इस तथ्य को ध्यान में रखता है और केवल विभाजित शब्दकोश के आकार की गणना करते समय मानों के अनुसार मेल करता है।

यह शुक्र है, स्वयं व्याख्यात्मक है:

Py_ssize_t size, res;

size = DK_SIZE(mp->ma_keys);
res = _PyObject_SIZE(Py_TYPE(mp));
if (mp->ma_values)                    /*Add the values to the result*/
    res += size * sizeof(PyObject*);
/* If the dictionary is split, the keys portion is accounted-for
   in the type object. */
if (mp->ma_keys->dk_refcnt == 1)     /* Add keys/hashes size to res */
    res += sizeof(PyDictKeysObject) + (size-1) * sizeof(PyDictKeyEntry);
return res;

जहाँ तक मुझे पता है, विभाजन-तालिका शब्दकोशों को केवल उदाहरणों के नेमस्पेस के लिए बनाया जाता है , dict() या {} (जैसा भी पीईपी में वर्णित है dict() का उपयोग करते हुए हमेशा एक संयुक्त शब्दकोश में होता है जिसमें इन लाभ नहीं होते हैं

एक तरफ, क्योंकि यह मजेदार है, हम हमेशा इस अनुकूलन को तोड़ सकते हैं। दो मौजूदा तरीके हैं जो मैंने वर्तमान में पाया है, एक मूर्खतापूर्ण तरीके से या अधिक समझदार परिदृश्य से:

  1. मूर्ख होने के नाते:

    >>> f = Foo(20, 30)
    >>> getsizeof(vars(f))
    96
    >>> vars(f).update({1:1})  # add a non-string key
    >>> getsizeof(vars(f))
    288

    स्प्लिट टेबल केवल स्ट्रिंग कुंजी का समर्थन करते हैं, जो एक गैर-स्ट्रिंग कुंजी (जो वास्तव में शून्य समझ में आता है) को जोड़ता है, इस नियम को तोड़ता है और CPython विभाजन तालिका को सभी मेमोरी लाभों को खोने के लिए एक संयुक्त में बदल देता है।

  2. ऐसा परिदृश्य जो हो सकता है:

    >>> f1, f2 = Foo(20, 30), Foo(30, 40)
    >>> for i, j in enumerate([f1, f2]):
    ...    setattr(j, 'i'+str(i), i)
    ...    print(getsizeof(vars(j)))
    96
    288

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

    # after running previous snippet
    >>> getsizeof(vars(Foo(100, 200)))
    288

ज़ाहिर है, इस उद्देश्य के लिए ऐसा करने के लिए मज़े के अलावा कोई अच्छा कारण नहीं है

यदि कोई भटक रहा है, तो पायथन 3.6 का शब्दकोश क्रियान्वयन इस तथ्य को बदल नहीं सकता है। शब्दकोशों के दो aforementioned रूपों जबकि अभी भी उपलब्ध हैं, बस आगे कॉम्पैक्ट ( dict.__sizeof__ का कार्यान्वयन भी बदल गया है, इसलिए कुछ मतभेदों को getsizeof मूल्यों में आ जाना चाहिए।)

पायथन में, एक वर्ग के उदाहरणों के लिए बनाए गए शब्दकोशों को उस क्लास के समान गुणों वाले बनाए गए शब्दकोशों की तुलना में बहुत कम है:

import sys

class Foo(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b

f = Foo(20, 30)

जब पायथन 3.5.2 का उपयोग करते हैं, तो निम्न का उत्पादन करता है:

>>> sys.getsizeof(vars(f))  # vars gets obj.__dict__
96 
>>> sys.getsizeof(dict(vars(f))
288

288 - 96 = 192 बाइट्स बचाए!

अजगर 2.7.12 का उपयोग करते हुए, दूसरी तरफ, वही कॉल वापस लौटाता है:

>>> sys.getsizeof(vars(f))
280
>>> sys.getsizeof(dict(vars(f)))
280

0 बाइट्स बचाए गए

दोनों ही मामलों में, शब्दकोशों में स्पष्ट रूप से एक ही सामग्री है :

>>> vars(f) == dict(vars(f))
True

तो यह एक कारक नहीं है। इसके अलावा, यह केवल पायथन 3 पर लागू होता है

तो, यहाँ क्या हो रहा है? पायथन 3 में एक उदाहरण के __dict__ का आकार क्यों छोटा है?





python-internals