मैं पायथन में किसी वस्तु का आकार कैसे निर्धारित करूं?
जवाब, "बस यूज sys.getizeof" एक पूर्ण जवाब नहीं है।
यह उत्तर बिल्ट ऑब्जेक्ट्स के लिए सीधे काम करता है , लेकिन यह उन चीज़ों के लिए खाता नहीं है, जिनमें विशेष रूप से कस्टम ऑब्जेक्ट्स, ट्यूपल्स, लिस्ट्स, डीकट्स और सेट्स जैसे प्रकार शामिल हो सकते हैं। उनमें एक-दूसरे के उदाहरण, साथ ही संख्याएँ, तार और अन्य वस्तुएँ हो सकती हैं।
एक और पूरा जवाब
एनाकोंडा वितरण से 64 बिट पायथन 3.6 का उपयोग करते हुए, sys.getizeof के साथ, मैंने निम्नलिखित वस्तुओं का न्यूनतम आकार निर्धारित किया है, और ध्यान दें कि स्पेस को सेट करता है और स्पेस देता है ताकि रिक्त राशि एक निर्धारित राशि के बाद फिर से न बढ़े। भाषा के कार्यान्वयन से भिन्न):
अजगर 3:
Empty
Bytes type scaling notes
28 int +4 bytes about every 30 powers of 2
37 bytes +1 byte per additional byte
49 str +1-4 per additional character (depending on max width)
48 tuple +8 per additional item
64 list +8 for each additional
224 set 5th increases to 736; 21nd, 2272; 85th, 8416; 341, 32992
240 dict 6th increases to 368; 22nd, 1184; 43rd, 2280; 86th, 4704; 171st, 9320
136 func def does not include default args and other attrs
1056 class def no slots
56 class inst has a __dict__ attr, same scaling as dict above
888 class def with slots
16 __slots__ seems to store in mutable tuple-like structure
first slot grows to 48, and so on.
आप इसकी व्याख्या कैसे करते हैं? वैसे कहें कि आपके पास इसमें 10 आइटम हैं। यदि प्रत्येक आइटम 100 बाइट्स है, तो संपूर्ण डेटा संरचना कितनी बड़ी है? यह सेट 736 है क्योंकि यह एक समय 736 बाइट्स का आकार ले चुका है। फिर आप आइटम का आकार जोड़ते हैं, ताकि कुल 1736 बाइट्स हो जाएं
फ़ंक्शन और वर्ग परिभाषाओं के लिए कुछ चेतावनी:
ध्यान दें कि प्रत्येक कक्षा की परिभाषा में कक्षा के लिए एक प्रॉक्सी __dict__
(48 बाइट्स) संरचना होती है। property
कक्षा की परिभाषा में प्रत्येक स्लॉट में एक डिस्क्रिप्टर (जैसे ) होता है।
स्लॉटेड उदाहरण अपने पहले तत्व पर 48 बाइट्स के साथ शुरू होते हैं, और 8 प्रत्येक अतिरिक्त बढ़ाते हैं। केवल खाली स्लेटेड ऑब्जेक्ट्स में 16 बाइट्स होते हैं, और बिना डेटा वाले एक उदाहरण बहुत कम समझ में आता है।
इसके अलावा, प्रत्येक फ़ंक्शन परिभाषा में कोड ऑब्जेक्ट्स, शायद डोकस्ट्रिंग्स और अन्य संभावित विशेषताएं हैं, यहां तक कि ए __dict__
।
यह भी ध्यान दें कि हम उपयोग करते हैं sys.getsizeof()
क्योंकि हम सीमांत अंतरिक्ष उपयोग के बारे में परवाह करते हैं, जिसमें डस्ट से ऑब्जेक्ट के लिए कचरा संग्रह शामिल है :
getizeof () ऑब्जेक्ट की __sizeof__
विधि को कॉल करता है और अतिरिक्त कचरा कलेक्टर ओवरहेड जोड़ता है यदि ऑब्जेक्ट को कचरा कलेक्टर द्वारा प्रबंधित किया जाता है।
यह भी ध्यान दें कि सूचियों का आकार बदलना (उदाहरण के लिए उन्हें दोहराते हुए) उन्हें स्पेसलोकेट करने के लिए प्रेरित करता है, इसी तरह सेट और डाइक को भी। से listobj.c स्रोत कोड :
/* 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, ...
* Note: new_allocated won't overflow because the largest possible value
* is PY_SSIZE_T_MAX * (9 / 8) + 6 which always fits in a size_t.
*/
new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6);
ऐतिहासिक आंकड़ा
पायथन 2.7 विश्लेषण, के साथ की पुष्टि की guppy.hpy
और sys.getsizeof
:
Bytes type empty + scaling notes
24 int NA
28 long NA
37 str + 1 byte per additional character
52 unicode + 4 bytes per additional character
56 tuple + 8 bytes per additional item
72 list + 32 for first, 8 for each additional
232 set sixth item increases to 744; 22nd, 2280; 86th, 8424
280 dict sixth item increases to 1048; 22nd, 3352; 86th, 12568 *
120 func def does not include default args and other attrs
64 class inst has a __dict__ attr, same scaling as dict above
16 __slots__ class with slots has no dict, seems to store in
mutable tuple-like structure.
904 class def has a proxy __dict__ structure for class attrs
104 old class makes sense, less stuff, has real dict though.
ध्यान दें कि शब्दकोशों ( लेकिन सेट नहीं ) को पायथन 3.6 में अधिक कॉम्पैक्ट प्रतिनिधित्व मिला
मुझे लगता है कि संदर्भ के लिए अतिरिक्त आइटम प्रति 8 बाइट्स 64 बिट मशीन पर बहुत अधिक समझ में आता है। उन 8 बाइट्स स्मृति में उस जगह को इंगित करते हैं जहां निहित आइटम है। 4 बाइट्स पायथन 2 में यूनिकोड के लिए निश्चित चौड़ाई हैं, अगर मुझे सही ढंग से याद है, लेकिन पायथन 3 में, स्ट्रेट पात्रों की अधिकतम चौड़ाई के बराबर चौड़ाई का यूनिकोड बन जाता है।
(और स्लॉट्स पर अधिक जानकारी के लिए, यह उत्तर देखें )
एक अधिक पूर्ण कार्य
हम एक ऐसा फंक्शन चाहते हैं, जो लिस्ट, टुपल्स, सेट्स, डिक्ट्स obj.__dict__
, और obj.__slots__
अन्य चीजों के साथ-साथ उन अन्य चीजों को भी खोजे, जिनके बारे में हमने अभी तक नहीं सोचा है।
हम gc.get_referents
इस खोज को करने के लिए भरोसा करना चाहते हैं क्योंकि यह सी स्तर पर काम करता है (इसे बहुत तेज बनाता है)। नकारात्मक पक्ष यह है कि get_referents निरर्थक सदस्यों को वापस कर सकते हैं, इसलिए हमें यह सुनिश्चित करने की आवश्यकता है कि हम डबल गिनती न करें।
कक्षाएं, मॉड्यूल और फ़ंक्शंस एकल हैं - वे एक बार मेमोरी में मौजूद होते हैं। हमें उनके आकार में इतनी दिलचस्पी नहीं है, क्योंकि हम उनके बारे में बहुत कुछ नहीं कर सकते हैं - वे कार्यक्रम का एक हिस्सा हैं। यदि हम संदर्भित होने पर उन्हें गिनने से बचेंगे।
हम प्रकारों की एक ब्लैकलिस्ट का उपयोग करने जा रहे हैं ताकि हम अपने आकार की गणना में पूरे कार्यक्रम को शामिल न करें।
import sys
from types import ModuleType, FunctionType
from gc import get_referents
# Custom objects know their class.
# Function objects seem to know way too much, including modules.
# Exclude modules as well.
BLACKLIST = type, ModuleType, FunctionType
def getsize(obj):
"""sum size of object & members."""
if isinstance(obj, BLACKLIST):
raise TypeError('getsize() does not take argument of type: '+ str(type(obj)))
seen_ids = set()
size = 0
objects = [obj]
while objects:
need_referents = []
for obj in objects:
if not isinstance(obj, BLACKLIST) and id(obj) not in seen_ids:
seen_ids.add(id(obj))
size += sys.getsizeof(obj)
need_referents.append(obj)
objects = get_referents(*need_referents)
return size
निम्नलिखित श्वेतसूचीबद्ध फ़ंक्शन के साथ इसके विपरीत, अधिकांश ऑब्जेक्ट्स जानते हैं कि कचरा संग्रह के प्रयोजनों के लिए खुद को कैसे पार करना है (जो लगभग वही है जो हम खोज रहे हैं जब हम यह जानना चाहते हैं कि मेमोरी कुछ वस्तुओं में कितनी महंगी है। इस कार्यक्षमता का उपयोग किया जाता है। gc.get_referents
।) हालांकि, यह उपाय हमारे द्वारा किए गए उद्देश्य से बहुत अधिक विस्तार में है, अगर हम सावधान नहीं हैं।
उदाहरण के लिए, फ़ंक्शन उन मॉड्यूल के बारे में बहुत कुछ जानते हैं जो वे बनाए गए हैं।
इसके विपरीत एक और बात यह है कि शब्दकोशों में कुंजी रखने वाले तार आमतौर पर इंटर्न किए जाते हैं ताकि वे डुप्लिकेट न हों। के लिए जाँच id(key)
हमें डुप्लिकेट गिनती से बचने की भी अनुमति देगा, जो हम अगले भाग में करते हैं। ब्लैकलिस्ट समाधान पूरी तरह से तार हैं कि गिनती गिनती रुक जाती है।
सफेद किए गए प्रकार, पुनरावर्ती आगंतुक (पुराना कार्यान्वयन)
इनमें से अधिकांश प्रकारों को स्वयं को कवर करने के लिए, जीसी मॉड्यूल पर निर्भर होने के बजाय, मैंने इस पुनरावर्ती कार्य को सबसे पायथन वस्तुओं के आकार का अनुमान लगाने का प्रयास करने के लिए लिखा, जिसमें अधिकांश बिलियन, संग्रह मॉड्यूल में प्रकार और कस्टम प्रकार (स्लॉटेड और अन्यथा) शामिल हैं। ।
इस प्रकार का फ़ंक्शन मेमोरी के उपयोग के लिए हमारे द्वारा गिने जाने वाले प्रकारों पर बहुत अधिक बारीक नियंत्रण देता है, लेकिन इसके प्रकारों को छोड़ने का खतरा है:
import sys
from numbers import Number
from collections import Set, Mapping, deque
try: # Python 2
zero_depth_bases = (basestring, Number, xrange, bytearray)
iteritems = 'iteritems'
except NameError: # Python 3
zero_depth_bases = (str, bytes, Number, range, bytearray)
iteritems = 'items'
def getsize(obj_0):
"""Recursively iterate to sum size of object & members."""
_seen_ids = set()
def inner(obj):
obj_id = id(obj)
if obj_id in _seen_ids:
return 0
_seen_ids.add(obj_id)
size = sys.getsizeof(obj)
if isinstance(obj, zero_depth_bases):
pass # bypass remaining control flow and return
elif isinstance(obj, (tuple, list, Set, deque)):
size += sum(inner(i) for i in obj)
elif isinstance(obj, Mapping) or hasattr(obj, iteritems):
size += sum(inner(k) + inner(v) for k, v in getattr(obj, iteritems)())
# Check for custom object instances - may subclass above too
if hasattr(obj, '__dict__'):
size += inner(vars(obj))
if hasattr(obj, '__slots__'): # can have __slots__ with __dict__
size += sum(inner(getattr(obj, s)) for s in obj.__slots__ if hasattr(obj, s))
return size
return inner(obj_0)
और मैंने इसे आकस्मिक रूप से परीक्षण किया (मुझे इसे एकतरफा करना चाहिए):
>>> getsize(['a', tuple('bcd'), Foo()])
344
>>> getsize(Foo())
16
>>> getsize(tuple('bcd'))
194
>>> getsize(['a', tuple('bcd'), Foo(), {'foo': 'bar', 'baz': 'bar'}])
752
>>> getsize({'foo': 'bar', 'baz': 'bar'})
400
>>> getsize({})
280
>>> getsize({'foo':'bar'})
360
>>> getsize('foo')
40
>>> class Bar():
... def baz():
... pass
>>> getsize(Bar())
352
>>> getsize(Bar().__dict__)
280
>>> sys.getsizeof(Bar())
72
>>> getsize(Bar.__dict__)
872
>>> sys.getsizeof(Bar.__dict__)
280
यह कार्यान्वयन वर्ग परिभाषाओं और कार्य परिभाषाओं पर टूट जाता है क्योंकि हम उनकी सभी विशेषताओं के बाद नहीं जाते हैं, लेकिन चूंकि उन्हें प्रक्रिया के लिए केवल एक बार स्मृति में मौजूद होना चाहिए, इसलिए उनका आकार वास्तव में बहुत अधिक मायने नहीं रखता है।