পাইথনে কোনও বস্তুর আকার কীভাবে নির্ধারণ করব?
"Just sys.getsizeof ব্যবহার করুন" উত্তরটি সম্পূর্ণ উত্তর নয়।
এই উত্তরটি অন্তর্নির্মিত অবজেক্টগুলির জন্য সরাসরি কাজ করে , তবে কাস্টম অবজেক্টস, টিপলস, তালিকা, ডিক্টস এবং সেটগুলিতে কী কী ধরণের জিনিসগুলি নির্দিষ্টভাবে থাকতে পারে তার জন্য এটি হিসাব করে না। এগুলিতে একে অপরের উদাহরণ, পাশাপাশি সংখ্যা, স্ট্রিং এবং অন্যান্য অবজেক্ট থাকতে পারে।
আরও একটি সম্পূর্ণ উত্তর
Sys.getsizeof সহ অ্যানাকোন্ডা বিতরণ থেকে bit৪ বিট পাইথন ৩.6 ব্যবহার করে আমি নিম্নলিখিত বিষয়গুলির সর্বনিম্ন আকার নির্ধারণ করেছি এবং নোট করুন যে সেট সেট এবং ডিক্টস প্রিলোকল্ট স্পেস তাই একটি নির্দিষ্ট পরিমাণের পরে খালিগুলি আবার বৃদ্ধি না পায় (যা হতে পারে ভাষা প্রয়োগের মাধ্যমে পরিবর্তিত হয়):
পাইথন 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 বাইট হয় তবে পুরো ডেটা স্ট্রাকচারটি কতটা বড়? সেটটি নিজেই 6৩6 কারণ এটি এক সময় আকার বাড়িয়ে 73৩6 বাইট করে। তারপরে আপনি আইটেমগুলির আকার যুক্ত করুন, সুতরাং এটি মোট 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.
দ্রষ্টব্য যে অভিধানগুলি ( তবে সেট নয় ) পাইথন ৩.6-তে আরও কমপ্যাক্ট উপস্থাপনা পেয়েছে
আমি মনে করি অতিরিক্ত আইটেম প্রতি 8 বাইট রেফারেন্স একটি 64 বিট মেশিনে প্রচুর পরিমাণে বোঝায়। এই 8 বাইটগুলি স্মৃতিতে স্থানটি নির্দেশ করে অন্তর্ভুক্ত আইটেমটি রয়েছে। পাইথন 2-তে ইউনিকোডের জন্য 4 বাইটগুলি স্থির করে দেওয়া হয়েছে, যদি আমি সঠিকভাবে স্মরণ করি তবে পাইথন 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
নিম্নলিখিত শ্বেত তালিকাভুক্ত ফাংশনটির সাথে এটির বিপরীতে, বেশিরভাগ অবজেক্টগুলি জঞ্জাল সংগ্রহের উদ্দেশ্যে কীভাবে নিজেদেরকে ট্র্যাভার করতে হয় তা জেনে থাকে (যা আমরা মেমরির কতটা ব্যয়বহুল তা কতটা ব্যয়বহুল তা জানতে চাইলে আমরা প্রায় সন্ধান করি This এই কার্যকারিতাটি ব্যবহার করে gc.get_referents
।) তবে, আমরা যদি যত্নবান না হই তবে এই ব্যবস্থাটি ক্ষেত্রের চেয়ে অনেক বেশি বিস্তৃত হতে চলেছে intended
উদাহরণস্বরূপ, ফাংশনগুলি সেগুলির মধ্যে তৈরি মডিউলগুলি সম্পর্কে যথেষ্ট পরিমাণে জানে।
এর বিপরীতে আরেকটি বিষয় হ'ল অভিধানে কীগুলি থাকে এমন স্ট্রিংগুলি সাধারণত ইন্টার্ন করা হয় যাতে সেগুলি নকল হয় না। অনুসন্ধান করা 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
এই বাস্তবায়ন শ্রেণীর সংজ্ঞা এবং ফাংশন সংজ্ঞাগুলিতে ভেঙে যায় কারণ আমরা তাদের সমস্ত গুণাবলী অনুসরণ করি না, তবে যেহেতু প্রক্রিয়াটির জন্য স্মৃতিতে তাদের কেবল একবার উপস্থিত হওয়া উচিত, তাই তাদের আকারটি খুব বেশি গুরুত্ব দেয় না।