2015-10-23 4 views
6

Мне любопытно узнать, как управление памятью отличается от Bytearray и списка в Python.Разница между Bytearray и List в Python

Я нашел несколько вопросов, таких как Difference between bytearray and list, но точно не ответил на мой вопрос.

Мой вопрос точно ...

from array import array 
>>> x = array("B", (1,2,3,4)) 
>>> x.__sizeof__() 
36 
>>> y = bytearray((1,2,3,4)) 
>>> y.__sizeof__() 
32 
>>> z = [1,2,3,4] 
>>> z.__sizeof__() 
36 

Как мы можем видеть, что есть разница в размерах между список/array.array (36 байт для 4-х элементов) и массив байт (32 байт для 4-х элементов). Может кто-нибудь объяснить мне, почему это? Для байтового массива имеет смысл, что он занимает 32 байт памяти для 4 элементов (4 * 8 == 32), но как это можно интерпретировать для списка и array.array?

# Lets take the case of bytearray (which makes more sense to me at least :p) 
for i in y: 
     print(i, ": ", id(i)) 

1 : 499962320 
2 : 499962336 #diff is 16 units 
3 : 499962352 #diff is 16 units 
4 : 499962368 #diff is 16 units 

Почему разница между двумя смежными элементами отличаются 16 единицами здесь, когда каждый элемент занимает всего 8 байт. Означает ли это, что каждый указатель адреса памяти указывает на кусок?

Также каковы критерии распределения памяти для целого числа? Я читал, что Python назначит больше памяти на основе значения целого числа (исправьте меня, если я ошибаюсь), так как чем больше число, тем больше памяти.

Например:

>>> y = 10 
>>> y.__sizeof__() 
14 
>>> y = 1000000 
>>> y.__sizeof__() 
16 
>>> y = 10000000000000 
>>> y.__sizeof__() 
18 

что критерии, которые Python выделяет память?

И почему Python занимает гораздо больше памяти, а C занимает всего 8 байтов (мой 64-разрядный компьютер)? когда они полностью находятся в диапазоне целых чисел (2 ** 64)?

Метаданные:

Python версии:'3.4.3 (v3.4.3:9b73f1c3e601, Feb 24 2015, 22:43:06) [MSC v.1600 32 bit (Intel)]'

машина арка: 64-разрядное

PS: Просьба направить меня на хорошую статью, где управление памятью Питон объяснил лучше. Я потратил почти час, чтобы понять эти вещи, и в конечном итоге спросил этот Вопрос в SO. :(

+2

Хороший вопрос, рекомендуется. Эй, вам повезло: на моей 64-разрядной машине Linux Xubuntu CPython 3.4.3 'y .__ sizeof __()' дает мне '28' для' y = 10', то же самое для 'y = 1M',' 32' for 'y = 10000000000000' – Pynchia

+0

Привет @Pynchia, Mine - это 32-разрядный питон, хотя моя машина - 64 бит. Я не уверен, но это может быть причиной. Дождитесь, пока кто-нибудь уточнит. –

ответ

1

Я не утверждаю, что это полный ответ, но есть некоторые намеки на понимание этого.

bytearray представляет собой последовательность байтов и list представляет собой последовательность ссылок на объекты. Таким образом, [1,2,3] на самом деле держит указатели памяти до тех чисел, которые хранятся в памяти в другом месте. для того, чтобы рассчитать общее потребление памяти структуры списка, мы можем сделать это (я использую sys.getsizeof везде дальше, звоню __sizeof__ плюс GC накладных расходов)

>>> x = [1,2,3] 
>>> sum(map(getsizeof, x)) + getsizeof(x) 
172 

Результат может быть d ifferent на разных машинах.

Кроме того, посмотрите на это:

>> getsizeof([]) 
64 

Это потому, что списки изменчивы. Чтобы быть быстрым, эта структура выделяет некоторую память диапазон для хранения ссылок на объекты (плюс некоторое хранилище для мета, например, длина списка). Когда вы добавляете элементы, следующие ячейки памяти заполняются ссылками на эти элементы. Когда нет места для хранения новых элементов, выделяется новый, больший диапазон, существуют данные, скопированные там, и старые выпущены. Это называется динамическими массивами.

Вы можете наблюдать за этим путем, выполнив этот код.

import sys 
data=[] 
n=15 
for k in range(n): 
    a = len(data) 
    b = sys.getsizeof(data) 
    print('Length: {0:3d}; Size in bytes: {1:4d}'.format(a, b)) 
    data.append(None) 

Мои результаты:

Length: 0; Size in bytes: 64 
Length: 1; Size in bytes: 96 
Length: 2; Size in bytes: 96 
Length: 3; Size in bytes: 96 
Length: 4; Size in bytes: 96 
Length: 5; Size in bytes: 128 
Length: 6; Size in bytes: 128 
Length: 7; Size in bytes: 128 
Length: 8; Size in bytes: 128 
Length: 9; Size in bytes: 192 
Length: 10; Size in bytes: 192 
Length: 11; Size in bytes: 192 
Length: 12; Size in bytes: 192 
Length: 13; Size in bytes: 192 
Length: 14; Size in bytes: 192 

Мы можем видеть, что есть 64 байт был использован для хранения 8 адресов памяти (64-бит каждый).

Почти то же самое относится к bytearray() (измените вторую строку на data = bytearray() и добавьте 1 в последнем).

Length: 0; Size in bytes: 56 
Length: 1; Size in bytes: 58 
Length: 2; Size in bytes: 61 
Length: 3; Size in bytes: 61 
Length: 4; Size in bytes: 63 
Length: 5; Size in bytes: 63 
Length: 6; Size in bytes: 65 
Length: 7; Size in bytes: 65 
Length: 8; Size in bytes: 68 
Length: 9; Size in bytes: 68 
Length: 10; Size in bytes: 68 
Length: 11; Size in bytes: 74 
Length: 12; Size in bytes: 74 
Length: 13; Size in bytes: 74 
Length: 14; Size in bytes: 74 

Разница в том, что память теперь используется для хранения фактических значений байтов, а не указателей.

Надеюсь, что поможет вам исследовать дальше.

+0

Привет @ anti1869, Спасибо за ваш комментарий. Его очень исчерпывающий и полезный. Но у меня есть следующие вопросы в вашем комментарии. Я не могу добавить всю информацию здесь и, следовательно, добавить еще один комментарий ниже. Спасибо –

+0

Его поняли для списка согласно вашему объяснению, но почему размер массива байтов начался с 56. и почему он стабилен после достижения 74? И также был бы рад, если бы вы могли дать больше информации, почему начальный размер, если 64 и 56. Спасибо –

+0

Проверьте, что исходный код структур данных. Там вы увидите внутреннюю структуру контейнера и что она выделяет память при инициализации. Также есть очень быстрый алгоритм роста. https://github.com/python/cpython/blob/master/Objects/listobject.c – anti1869

Смежные вопросы