ฉันจะกำหนดขนาดของวัตถุใน Python ได้อย่างไร


683

ฉันต้องการทราบวิธีรับขนาดของวัตถุเช่นสตริงจำนวนเต็ม ฯลฯ ใน Python

คำถามที่เกี่ยวข้อง: มีกี่ไบต์ต่อองค์ประกอบในรายการ Python (tuple)

ฉันใช้ไฟล์ XML ซึ่งมีฟิลด์ขนาดที่ระบุขนาดของค่า ฉันต้องแยกวิเคราะห์ XML นี้และทำการเข้ารหัสของฉัน เมื่อฉันต้องการเปลี่ยนค่าของฟิลด์ใดฟิลด์หนึ่งฉันจะตรวจสอบฟิลด์ขนาดของค่านั้น ที่นี่ฉันต้องการเปรียบเทียบว่าค่าใหม่ที่ฉันป้อนฆ้องมีขนาดเดียวกันกับใน XML ฉันต้องตรวจสอบขนาดของค่าใหม่ ในกรณีของสตริงฉันสามารถพูดความยาวได้ แต่ในกรณีของ int, float ฯลฯ ฉันสับสน

คำตอบ:


665

เพียงใช้ฟังก์ชันsys.getsizeof ที่กำหนดไว้ในsysโมดูล

sys.getsizeof(object[, default]):

ส่งคืนขนาดของวัตถุเป็นไบต์ วัตถุสามารถเป็นวัตถุประเภทใดก็ได้ วัตถุในตัวทั้งหมดจะส่งคืนผลลัพธ์ที่ถูกต้อง แต่ไม่จำเป็นต้องถือเป็นจริงสำหรับส่วนขยายของบุคคลที่สามเนื่องจากเป็นการใช้งานที่เฉพาะเจาะจง

อาร์กิวเมนต์จะช่วยให้การกำหนดค่าที่จะถูกส่งกลับถ้าชนิดของวัตถุไม่ได้ให้หมายถึงการดึงขนาดและจะทำให้เกิดdefault TypeError

getsizeofเรียกใช้__sizeof__เมธอดของวัตถุ และเพิ่มโอเวอร์เฮดตัวรวบรวมขยะเพิ่มเติมหากอ็อบเจ็กต์ได้รับการจัดการโดยตัวรวบรวมขยะ

ตัวอย่างการใช้งานใน python 3.0:

>>> import sys
>>> x = 2
>>> sys.getsizeof(x)
24
>>> sys.getsizeof(sys.getsizeof)
32
>>> sys.getsizeof('this')
38
>>> sys.getsizeof('this also')
48

หากคุณอยู่ในหลาม <2.6 และไม่มีsys.getsizeofคุณสามารถใช้โมดูลที่ครอบคลุมนี้แทน ไม่เคยใช้มัน


182
กรุณาเพิ่มที่จะปฏิเสธว่ามันจะไม่ถือเป็นจริงสำหรับวัตถุที่ซ้อนกันหรือ dicts ซ้อนกันหรือ dicts ในรายการอื่น ๆ
JohnnyM

8
@ChaimG นั่นเป็นเพราะวัตถุทุกชิ้นใช้ 32 ไบต์เท่านั้น !! ส่วนที่เหลือเป็นการอ้างอิงไปยังวัตถุอื่น หากคุณต้องการบัญชีสำหรับวัตถุอ้างอิงคุณต้องกำหนด__sizeof__วิธีการสำหรับชั้นเรียนของคุณ ในตัวชั้นหลามไม่กำหนดมันที่ว่าทำไมคุณจะได้รับผลที่ถูกต้องเมื่อมีการใช้วัตถุชนิดdict dict
nosklo

19
ข้อจำกัดความรับผิดชอบและข้อยกเว้นสำหรับการทำงานนี้ครอบคลุมเกือบทุกกรณีการใช้งานทำให้getsizeofฟังก์ชั่นมีค่าน้อยออกจากกล่อง
Robino

7
ทำไมจำนวนเต็ม 2 ถึง 24 ไบต์
Saher Ahwal

4
@SaherAhwal มันไม่ได้เป็นเพียงจำนวนเต็ม แต่เป็นวัตถุที่เต็มไปด้วยวิธีการคุณลักษณะที่อยู่ ...
nosklo

370

ฉันจะกำหนดขนาดของวัตถุใน Python ได้อย่างไร

คำตอบ "เพียงแค่ใช้ sys.getsizeof" ไม่ใช่คำตอบที่สมบูรณ์

คำตอบนั้นใช้ได้กับวัตถุบิวด์อินโดยตรง แต่ไม่ได้อธิบายถึงสิ่งที่วัตถุเหล่านั้นอาจมีโดยเฉพาะประเภทใดเช่นวัตถุที่กำหนดเอง tuples รายการ dicts และชุดประกอบด้วย พวกเขาสามารถมีอินสแตนซ์ซึ่งกันและกันรวมทั้งตัวเลขสตริงและวัตถุอื่น ๆ

คำตอบที่สมบูรณ์มากขึ้น

การใช้ 64 บิต Python 3.6 จากการกระจาย Anaconda ด้วย sys.getsizeof ฉันได้กำหนดขนาดต่ำสุดของวัตถุต่อไปนี้และโปรดทราบว่าชุดและกำหนดพื้นที่ preicocate preicocate เพื่อให้ว่างเปล่าไม่เติบโตอีกจนกว่าจะถึงจำนวนที่กำหนด (ซึ่งอาจ แตกต่างกันไปตามการใช้งานของภาษา):

Python 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) ในนิยามคลาส

อินสแตนซ์แบบ Slotted เริ่มต้นด้วย 48 ไบต์ในองค์ประกอบแรกของพวกเขาและเพิ่มขึ้น 8 แต่ละเพิ่มเติม วัตถุ slotted ที่ว่างเปล่าเท่านั้นที่มีขนาด 16 ไบต์และอินสแตนซ์ที่ไม่มีข้อมูลทำให้เข้าใจได้น้อยมาก

นอกจากนี้ยังมีฟังก์ชั่นความหมายแต่ละคนมีวัตถุรหัสอาจ docstrings และคุณลักษณะที่เป็นไปได้อื่น ๆ __dict__แม้กระทั่ง

นอกจากนี้โปรดทราบว่าเราใช้sys.getsizeof()เพราะเราใส่ใจเกี่ยวกับการใช้พื้นที่ส่วนเพิ่มซึ่งรวมถึงค่าใช้จ่ายในการรวบรวมขยะสำหรับวัตถุจากเอกสาร :

getsizeof () เรียกใช้__sizeof__เมธอดของวัตถุและเพิ่มโอเวอร์เฮดตัวรวบรวมขยะเพิ่มเติมหากอ็อบเจ็กต์ได้รับการจัดการโดยตัวรวบรวมขยะ

นอกจากนี้โปรดทราบว่าการปรับขนาดรายการ (เช่นการต่อท้ายรายการซ้ำ) ทำให้รายการจัดสรรพื้นที่ล่วงหน้าคล้ายกับชุดและ dicts จาก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);

ข้อมูลทางประวัติศาสตร์

การวิเคราะห์ Python 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.

โปรดทราบว่าพจนานุกรม ( แต่ไม่ได้ตั้งค่า ) จะมีการนำเสนอที่กระชับยิ่งขึ้นใน Python 3.6

ฉันคิดว่า 8 ไบต์ต่อรายการเพิ่มเติมสำหรับการอ้างอิงนั้นสมเหตุสมผลกับเครื่อง 64 บิต 8 ไบต์เหล่านั้นชี้ไปยังสถานที่ในหน่วยความจำที่มีรายการนั้นอยู่ที่ 4 ไบต์เป็นความกว้างคงที่สำหรับ Unicode ใน Python 2 ถ้าฉันจำได้อย่างถูกต้อง แต่ใน Python 3 str กลายเป็น Unicode ของความกว้างเท่ากับความกว้างสูงสุดของอักขระ

(และสำหรับช่องเพิ่มเติมดูคำตอบนี้ )

ฟังก์ชั่นที่สมบูรณ์ยิ่งขึ้น

เราต้องการฟังก์ชั่นที่ค้นหาองค์ประกอบในรายการสิ่งอันดับชุด dicts obj.__dict__ของและobj.__slots__ตลอดจนสิ่งอื่น ๆ ที่เราอาจยังไม่เคยนึกถึง

เราต้องการที่จะใช้gc.get_referentsการค้นหานี้เพราะมันทำงานในระดับ C (ทำให้มันเร็วมาก) ข้อเสียคือที่ 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)จะทำให้เราสามารถหลีกเลี่ยงการนับซ้ำซึ่งเราทำในส่วนถัดไป โซลูชันบัญชีดำข้ามคีย์การนับที่เป็นสตริงโดยสิ้นเชิง

ประเภทที่อนุญาต, ผู้เยี่ยมชมซ้ำ (การใช้งานแบบเก่า)

เพื่อครอบคลุมประเภทเหล่านี้ส่วนใหญ่แทนที่จะอาศัยโมดูล gc ฉันได้เขียนฟังก์ชันเรียกซ้ำนี้เพื่อพยายามประเมินขนาดของวัตถุ Python ส่วนใหญ่รวมถึง builtins ส่วนใหญ่ประเภทในโมดูลการรวบรวมและประเภทที่กำหนดเอง (slotted และอื่น ๆ ) .

ฟังก์ชั่นประเภทนี้ให้การควบคุมที่ละเอียดยิ่งกว่าประเภทที่เราจะนับสำหรับการใช้หน่วยความจำ แต่มีอันตรายจากการทิ้งประเภท:

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)

และฉันทดสอบมันค่อนข้างตั้งใจ (ฉันควร unittest):

>>> 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

การนำไปใช้งานนี้แบ่งย่อยตามคำจำกัดความของคลาสและนิยามฟังก์ชันเนื่องจากเราไม่ได้ดำเนินการตามคุณลักษณะทั้งหมดของพวกเขา แต่เนื่องจากพวกเขาควรมีอยู่เพียงครั้งเดียวในหน่วยความจำสำหรับกระบวนการขนาดของพวกเขาไม่สำคัญมากนัก


5
คุณอาจเพิ่มว่าคำตอบนี้เฉพาะเจาะจงกับ CPython (ซึ่งโดยนัยแล้วว่าคุณได้รับ Python ผ่าน Anaconda)
41919

1
CPython เป็นการดำเนินการอ้างอิงและฉันเพิ่งตรวจสอบเอกสารออนไลน์ของ jython ซึ่งมี API เดียวกันดังนั้นฉันเชื่อว่าสิ่งนี้จะทำงานได้กับการใช้งานอื่น ๆ ตราบใดที่พวกเขาใช้ API
Aaron Hall

สำหรับฉันไม่ได้ทำงานให้กับอาร์เรย์ที่สวมหน้ากากแล้วและไม่ได้เปิดโปงstackoverflow.com/q/58675479/2132157
GM

96

Pymplerแพคเกจของasizeofโมดูลสามารถทำเช่นนี้

ใช้ดังต่อไปนี้:

from pympler import asizeof
asizeof.asizeof(my_object)

sys.getsizeofมันไม่เหมือนกับวัตถุที่คุณสร้างขึ้นเอง มันใช้งานได้ดีด้วย

>>> asizeof.asizeof(tuple('bcd'))
200
>>> asizeof.asizeof({'foo': 'bar', 'baz': 'bar'})
400
>>> asizeof.asizeof({})
280
>>> asizeof.asizeof({'foo':'bar'})
360
>>> asizeof.asizeof('foo')
40
>>> asizeof.asizeof(Bar())
352
>>> asizeof.asizeof(Bar().__dict__)
280
>>> A = rand(10)
>>> B = rand(10000)
>>> asizeof.asizeof(A)
176
>>> asizeof.asizeof(B)
80096

ในฐานะที่เป็นที่กล่าวถึง ,

(การไบต์) รหัสขนาดของวัตถุเช่นการเรียนการทำงานวิธีการโมดูลอื่น ๆ code=Trueสามารถรวมอยู่ด้วยตัวเลือกการตั้งค่า

และหากคุณต้องการมุมมองอื่น ๆ เกี่ยวกับข้อมูลสด Pympler's

โมดูลmuppyใช้สำหรับการตรวจสอบแบบออนไลน์ของแอปพลิเคชั่น Python และโมดูลClass Trackerให้การวิเคราะห์แบบออฟไลน์ของอายุการใช้งานของวัตถุ Python ที่เลือก


ฟังก์ชั่นนี้ค่อนข้างช้าสำหรับวัตถุขนาดใหญ่ มีเทียบเท่า "เร็ว" ที่ทำงานกับวัตถุที่สร้างขึ้นด้วยตนเองหรือไม่?
Shuklaswag

ฉันยังไม่ได้ทดสอบ แต่org.apache.spark.util.SizeEstimatorอาจเกี่ยวข้อง
Shuklaswag

1
@Shuklaswag: ถ้าคุณใช้ประกายไฟก็อาจเป็นไปได้ คุณคิดว่าการประมาณการแปลง + Javaเร็วกว่าวิธีในตัวของงูใหญ่หรือไม่? หรือฉันเข้าใจผิด?
serv-inc

3
อาจเป็นที่น่าสังเกตว่าpymplerมีความสามารถในการใช้ขนาดรหัสที่สามารถเรียกใช้งานได้ของฟังก์ชั่นและ callables และวัตถุรหัสอื่น ๆ
mtraceur

ฉันได้รับTypeErrorข้อยกเว้น "วัตถุ 'NoneType' ไม่ callable" เมื่อใดก็ตามที่วัตถุที่กำหนดเองของฉันมีบางอย่างใน subobject "ต้นไม้" Noneที่มีค่า มีวิธีแก้ไขปัญหาด่วนนี้หรือไม่?
James Hirschorn

81

สำหรับอาร์เรย์ numpy getsizeofไม่ทำงาน - สำหรับฉันแล้วจะคืนค่า 40 ด้วยเหตุผลบางอย่าง:

from pylab import *
from sys import getsizeof
A = rand(10)
B = rand(10000)

จากนั้น (ใน ipython):

In [64]: getsizeof(A)
Out[64]: 40

In [65]: getsizeof(B)
Out[65]: 40

อย่างมีความสุขแม้ว่า:

In [66]: A.nbytes
Out[66]: 80

In [67]: B.nbytes
Out[67]: 80000

29
> วัตถุในตัวทั้งหมดจะส่งคืนผลลัพธ์ที่ถูกต้อง แต่ไม่จำเป็นต้องถือเป็นจริงสำหรับส่วนขยายของบุคคลที่สามเนื่องจากเป็นการนำไปใช้งานที่เฉพาะ docs.python.org/library/sys.html#sys.getsizeof
warvariuc

33
"หากคุณใช้อาร์เรย์ numpy ( docs.scipy.org/doc/numpy/reference/arrays.ndarray.html ) คุณสามารถใช้แอตทริบิวต์ 'ndarray.nbytes' เพื่อประเมินขนาดในหน่วยความจำได้" stackoverflow.com/a/15591157/556413
glarrain

17
ฉันเดาว่า 40 ไบต์นั้นถูกต้อง แต่getsizeof()จะให้ขนาดของวัตถุ (ส่วนหัวของอาร์เรย์) ไม่ใช่ข้อมูลภายใน เช่นเดียวกับภาชนะหลามที่sys.getsizeof([1,2,4]) == sys.getsizeof([1,123**456,4]) == 48ในขณะที่sys.getsizeof(123**456) = 436
yota

3
ดูเหมือนว่าgetsizeof()ฟังก์ชั่นจะเปลี่ยนไปในบางจุดเพื่อคืนค่าที่คาดไว้
dshin

16

Python 3.8 (Q1 2019) จะเปลี่ยนแปลงผลลัพธ์บางส่วนsys.getsizeofตามที่ประกาศโดย Raymond Hettinger:

คอนเทนเนอร์ Python มีขนาดเล็กลง 8 ไบต์ในการสร้าง 64 บิต

tuple ()  48 -> 40       
list  []  64 ->56
set()    224 -> 216
dict  {} 240 -> 232

ปัญหานี้เกิดขึ้นหลังจากรุ่น 33597และInada Naoki ( methane)ทำงานกับ Compact PyGC_Head และPR 7043

ความคิดนี้ช่วยลดขนาด PyGC_Head สองคำ

ปัจจุบันใช้ PyGC_Head สามคำ ; gc_prev, และgc_nextgc_refcnt

  • gc_refcnt จะใช้เมื่อรวบรวมสำหรับการลบการทดลองใช้
  • gc_prev ใช้สำหรับการติดตามและยกเลิกการติดตาม

ดังนั้นหากเราสามารถหลีกเลี่ยงการติดตาม / ไม่ได้ติดตามในขณะที่ทำการทดลองลบ gc_prevและgc_refcntสามารถแชร์พื้นที่หน่วยความจำเดียวกันได้

ดู คอมมิท d5c875b :

ลบออกหนึ่งสมาชิกจากPy_ssize_t ออบเจ็กต์ที่ถูกติดตามของ GC ทั้งหมด (เช่น tuple, list, dict) จะลดขนาดลง 4 หรือ 8 ไบต์PyGC_Head


14

สิ่งนี้อาจซับซ้อนกว่าที่ดูขึ้นอยู่กับว่าคุณต้องการนับจำนวนสิ่งของอย่างไร ตัวอย่างเช่นถ้าคุณมีรายการของ ints คุณต้องการขนาดของรายการที่มีการอ้างอิงถึง ints หรือไม่? (เช่นรายการเท่านั้นไม่ใช่สิ่งที่มีอยู่ในนั้น) หรือคุณต้องการรวมข้อมูลจริงที่ชี้ไปในกรณีนี้คุณต้องจัดการกับการอ้างอิงที่ซ้ำกันและวิธีการป้องกันการนับซ้ำเมื่อวัตถุสองรายการมีการอ้างอิงถึง วัตถุเดียวกัน

คุณอาจต้องการดูที่หนึ่งในโปรไฟล์หน่วยความจำไพ ธ อนเช่นpysizerเพื่อดูว่าพวกเขาตอบสนองความต้องการของคุณหรือไม่


10

เมื่อเจอปัญหานี้หลายครั้งฉันก็เขียนฟังก์ชั่นเล็ก ๆ (ได้แรงบันดาลใจจากคำตอบของ @ aaron-hall) และการทดสอบที่ทำในสิ่งที่ฉันคาดหวังไว้ว่า sys.getsizeof:

https://github.com/bosswissam/pysize

หากคุณสนใจ backstory ตรงนี้ล่ะ

แก้ไข: แนบรหัสด้านล่างเพื่อการอ้างอิงได้ง่าย หากต้องการดูรหัสที่ทันสมัยที่สุดโปรดตรวจสอบลิงค์ github

    import sys

    def get_size(obj, seen=None):
        """Recursively finds size of objects"""
        size = sys.getsizeof(obj)
        if seen is None:
            seen = set()
        obj_id = id(obj)
        if obj_id in seen:
            return 0
        # Important mark as seen *before* entering recursion to gracefully handle
        # self-referential objects
        seen.add(obj_id)
        if isinstance(obj, dict):
            size += sum([get_size(v, seen) for v in obj.values()])
            size += sum([get_size(k, seen) for k in obj.keys()])
        elif hasattr(obj, '__dict__'):
            size += get_size(obj.__dict__, seen)
        elif hasattr(obj, '__iter__') and not isinstance(obj, (str, bytes, bytearray)):
            size += sum([get_size(i, seen) for i in obj])
        return size

7

นี่คือสคริปต์ด่วนที่ฉันเขียนโดยยึดคำตอบก่อนหน้านี้สำหรับขนาดรายการของตัวแปรทั้งหมด

for i in dir():
    print (i, sys.getsizeof(eval(i)) )

มันไม่ผิดมันคลุมเครือ sys.getsizeof จะส่งคืนค่าที่ต้องการเสมอดังนั้นจึงไม่จำเป็นต้องลดประสิทธิภาพด้วยการลอง .. ยกเว้น
der_fenix

โอ้มันเป็นจุดที่ดีและฉันไม่ได้คิดถึงมัน - รหัสในรูปแบบตอนนี้แสดงให้เห็นว่ามันถูกเขียนตามลำดับเหตุการณ์อย่างไร - ตอนแรกฉันรู้เกี่ยวกับก้อน (ดังนั้น nbytes) จากนั้นฉันค้นหาวิธีแก้ปัญหาทั่วไปเพิ่มเติม . ขอบคุณสำหรับคำอธิบาย _ / \ _
alexey

7

คุณสามารถทำให้วัตถุเป็นอนุกรมเพื่อให้ได้มาซึ่งการวัดที่เกี่ยวข้องอย่างใกล้ชิดกับขนาดของวัตถุ:

import pickle

## let o be the object, whose size you want to measure
size_estimate = len(pickle.dumps(o))

หากคุณต้องการวัดวัตถุที่ไม่สามารถดอง (เช่นเนื่องจากแลมบ์ดานิพจน์) cloudpickle อาจเป็นวิธีแก้ปัญหา


4

ใช้sys.getsizeof ()ถ้าคุณไม่ต้องการรวมขนาดของวัตถุที่เชื่อมโยง (ซ้อนอยู่)

อย่างไรก็ตามหากคุณต้องการนับวัตถุย่อยที่ซ้อนกันในรายการ, dicts, ชุด, tuples - และโดยปกตินี่คือสิ่งที่คุณกำลังมองหา - ใช้ฟังก์ชั่นrecursive deep sizeof ()ดังแสดงด้านล่าง:

import sys
def sizeof(obj):
    size = sys.getsizeof(obj)
    if isinstance(obj, dict): return size + sum(map(sizeof, obj.keys())) + sum(map(sizeof, obj.values()))
    if isinstance(obj, (list, tuple, set, frozenset)): return size + sum(map(sizeof, obj))
    return size

นอกจากนี้คุณยังสามารถหาฟังก์ชั่นนี้ได้ในกล่องเครื่องมือที่เก๋ไก๋พร้อมกับประโยชน์อื่น ๆ อีกมากมาย:

https://github.com/mwojnars/nifty/blob/master/util.py


3

หากคุณไม่ต้องการขนาดที่แน่นอนของวัตถุ แต่จะรู้ว่าใหญ่เพียงใดวิธีที่รวดเร็ว (และสกปรก) คือการอนุญาตให้โปรแกรมรันโหมดสลีปเป็นระยะเวลานานและตรวจสอบการใช้หน่วยความจำ (เช่น : การตรวจสอบกิจกรรมของ Mac) โดยกระบวนการไพ ธ อนนี้โดยเฉพาะ สิ่งนี้จะมีผลเมื่อคุณพยายามหาขนาดของวัตถุขนาดใหญ่หนึ่งชิ้นในกระบวนการหลาม ตัวอย่างเช่นเมื่อเร็ว ๆ นี้ฉันต้องการตรวจสอบการใช้หน่วยความจำของโครงสร้างข้อมูลใหม่และเปรียบเทียบกับโครงสร้างข้อมูลชุดของ Python ก่อนอื่นฉันเขียนองค์ประกอบ (คำจากหนังสือโดเมนสาธารณะขนาดใหญ่) ไปยังชุดจากนั้นตรวจสอบขนาดของกระบวนการแล้วทำสิ่งเดียวกันกับโครงสร้างข้อมูลอื่น ฉันพบว่ากระบวนการ Python พร้อมชุดหนึ่งใช้หน่วยความจำมากเป็นสองเท่าของโครงสร้างข้อมูลใหม่ อีกครั้งคุณจะไม่ ไม่สามารถบอกได้ว่าหน่วยความจำที่ใช้โดยกระบวนการเท่ากับขนาดของวัตถุ เมื่อขนาดของวัตถุใหญ่ขึ้นสิ่งนี้จะเข้าใกล้เมื่อหน่วยความจำที่เหลือในกระบวนการใช้หมดไปเมื่อเปรียบเทียบกับขนาดของวัตถุที่คุณพยายามตรวจสอบ


1
คำถามถามว่าจะทำอย่างไรในไพธ อนไม่ใช่แค่การค้นหาการใช้หน่วยความจำของออบเจ็กต์หลามและใช้มอนิเตอร์กิจกรรมของ Mac หรือซอฟต์แวร์อื่น ๆ ที่คล้ายคลึงกัน ที่ถูกกล่าวว่าการตรวจสอบการใช้งานหน่วยความจำของกระบวนการหลามด้วยวิธีนี้โดยทั่วไปเป็นวิธีที่ดีเพื่อให้แน่ใจว่าไม่มีอะไรผิดพลาด ...
Tom Wyllie

@ TomWyllie ขอบคุณ แต่การลบคำตอบนี้ถือเป็นความหมายเชิงลบว่าคำตอบนั้นผิดและไม่ได้ทำอะไรเลย วิธีที่ฉันพูดถึงอาจไม่สามารถนำมาใช้ใน Python ได้ แต่เป็นวิธีที่สะดวกในการประมาณขนาดของวัตถุ Python อย่างคร่าวๆ ฉันรู้ว่าฉันไม่ได้ตอบคำถามที่ถูกต้องอย่างไรก็ตามวิธีการนี้อาจมีประโยชน์สำหรับคนอื่นที่จะได้รับผลลัพธ์ที่คล้ายกัน
picmate 涅

1

คุณสามารถใช้ประโยชน์จาก getSizeof () ตามที่ระบุไว้ด้านล่างเพื่อกำหนดขนาดของวัตถุ

import sys
str1 = "one"
int_element=5
print("Memory size of '"+str1+"' = "+str(sys.getsizeof(str1))+ " bytes")
print("Memory size of '"+ str(int_element)+"' = "+str(sys.getsizeof(int_element))+ " bytes")

0

ฉันใช้เคล็ดลับนี้ ... อาจไม่แม่นยำกับวัตถุขนาดเล็ก แต่ฉันคิดว่ามันแม่นยำกว่าสำหรับวัตถุที่ซับซ้อน (เช่นพื้นผิว pygame) มากกว่า sys.getsizeof ()

import pygame as pg
import os
import psutil
import time


process = psutil.Process(os.getpid())
pg.init()    
vocab = ['hello', 'me', 'you', 'she', 'he', 'they', 'we',
         'should', 'why?', 'necessarily', 'do', 'that']

font = pg.font.SysFont("monospace", 100, True)

dct = {}

newMem = process.memory_info().rss  # don't mind this line
Str = f'store ' + f'Nothing \tsurface use about '.expandtabs(15) + \
      f'0\t bytes'.expandtabs(9)  # don't mind this assignment too

usedMem = process.memory_info().rss

for word in vocab:
    dct[word] = font.render(word, True, pg.Color("#000000"))

    time.sleep(0.1)  # wait a moment

    # get total used memory of this script:
    newMem = process.memory_info().rss
    Str = f'store ' + f'{word}\tsurface use about '.expandtabs(15) + \
          f'{newMem - usedMem}\t bytes'.expandtabs(9)

    print(Str)
    usedMem = newMem

บน windows 10 ของฉัน python 3.7.3 ผลลัพธ์คือ:

store hello          surface use about 225280    bytes
store me             surface use about 61440     bytes
store you            surface use about 94208     bytes
store she            surface use about 81920     bytes
store he             surface use about 53248     bytes
store they           surface use about 114688    bytes
store we             surface use about 57344     bytes
store should         surface use about 172032    bytes
store why?           surface use about 110592    bytes
store necessarily    surface use about 311296    bytes
store do             surface use about 57344     bytes
store that           surface use about 110592    bytes
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.