Python — Сколько весит ссылка в list
Сама ссылка — 4 байта на 32 разряда, 8 байт на 64 разряда. @andreymal прав:-) Можно проверить самому (проверка ниже — на 64-разрядной системе):
#56 байт выделено на пустой список, 64 - на одно число, 72 - на два import sys print(sys.getsizeof([])) #56 print(sys.getsizeof([12345678])) #64 print(sys.getsizeof([12345678, 2.844])) #72
Отслеживать
ответ дан 3 апр 2022 в 17:18
4,925 6 6 золотых знаков 11 11 серебряных знаков 29 29 бронзовых знаков
-
Важное на Мете
Похожие
Подписаться на ленту
Лента вопроса
Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.
Дизайн сайта / логотип © 2023 Stack Exchange Inc; пользовательские материалы лицензированы в соответствии с CC BY-SA . rev 2023.10.27.43697
Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.
Определение размера объекта в Python
Иногда возникает необходимость определить, сколько памяти занимает объект в Python. Это может быть полезно для оптимизации использования ресурсов, особенно в больших и сложных проектах.
Например, представим, что имеется большой список чисел, и возникает вопрос о том, сколько памяти он занимает.
Решение
В Python для определения размера объекта в памяти можно использовать модуль sys и его функцию getsizeof() .
import sys nums = [1, 2, 3, 4, 5] print(sys.getsizeof(nums))
В данном примере, функция getsizeof() возвращает размер списка nums в байтах.
Нюансы использования
Важно отметить, что getsizeof() возвращает только базовый размер объекта и не учитывает дополнительную память, занимаемую элементами объекта.
Например, для списка, getsizeof() вернет только размер самого списка, не учитывая память, которую занимают его элементы. Для получения общего размера списка и его элементов, необходимо будет просуммировать размеры всех элементов.
Также следует помнить, что функция getsizeof() работает не со всеми типами данных. Например, она не может определить размер пользовательских классов или функций.
Заключение
Определение размера объекта в Python может быть полезным инструментом при оптимизации использования ресурсов. Однако, стоит помнить о некоторых ограничениях и нюансах при использовании функции getsizeof() .
Python потребляет много памяти или как уменьшить размер объектов?
Проблема памяти может возникнуть, когда в процессе выполнения программы нужно иметь большое количество объектов, особенно если есть ограничения на общий размер доступной оперативной памяти.
Ниже приводится обзор некоторых методов уменьшения размера объектов, которые позволяют существенно сократить объем оперативной памяти, необходимой для программ на чистом Python.
Для простоты будем рассматривать структуры в Python для представления точки с координатами x , y , z с доступом к значениям координат по имени.
Dict
В небольших программах, особенно в скриптах, довольно просто и удобно использовать встроенный dict для представления структурной информации:
>>> ob = >>> x = ob['x'] >>> ob['y'] = y
С появлением более «компактной» реализации в Python 3.6 с упорядоченным набором ключей dict стал еще более привлекательным. Однако, посмотрим на размер его следа в оперативной памяти:
>>> print(sys.getsizeof(ob)) 240
Он занимает много памяти, особенно, если вдруг понадобится создать большое число экземпляров:
Количество экземпляров | Размер следа |
---|---|
1 000 000 | 240 Мб |
10 000 000 | 2.40 Гб |
100 000 000 | 24 Гб |
Class instance
Для любителей все облекать в классы более предпочтительным является определение в виде класса с доступом по имени атрибута:
class Point: # def __init__(self, x, y, z): self.x = x self.y = y self.z = z >>> ob = Point(1,2,3) >>> x = ob.x >>> ob.y = y
Интересна структура экземпляра класса:
Поле | Размер (байт) |
---|---|
PyGC_Head | 24 |
PyObject_HEAD | 16 |
__weakref__ | 8 |
__dict__ | 8 |
ВСЕГО: | 56 |
Здесь __weakref__ это ссылка на список, так называемых, слабых ссылок (weak reference) на данный объект, поле __dict__ это ссылка на словарь экземпляра класса, в котором содержатся значения атрибутов экземпляра (заметим, что ссылки на 64-битной платформе занимают 8 байт). Начиная с Python 3.3, используется общее пространство для хранения ключей в словаре для всех экземпляров класса. Это сокращает размер следа экземпляра в памяти:
>>> print(sys.getsizeof(ob), sys.getsizeof(ob.__dict__)) 56 112
Как результате большое количество экземпляров класса оставляют меньший след в памяти, чем обычный словарь ( dict ):
Количество экземпляров | Размер следа |
---|---|
1 000 000 | 168 Мб |
10 000 000 | 1.68 Гб |
100 000 000 | 16.8 Гб |
Нетрудно заметить, что след экземпляра в памяти все еще велик из-за размера словаря экземпляра.
Instance of class with __slots__
Существенное уменьшение следа экземпляра в памяти достигается путем исключения __dict__ и __weakref__ . Это возможно при помощи «трюка» со __slots__ :
class Point: __slots__ = 'x', 'y', 'z' def __init__(self, x, y, z): self.x = x self.y = y self.z = z >>> ob = Point(1,2,3) >>> print(sys.getsizeof(ob)) 64
След в памяти стал существенно компактнее:
Поле | Размер (байт) |
---|---|
PyGC_Head | 24 |
PyObject_HEAD | 16 |
x | 8 |
y | 8 |
z | 8 |
ВСЕГО: | 64 |
Использование __slots__ в определении класса приводит к тому, что след большого числа экземпляров в памяти существенно уменьшается:
Количество экземпляров | Размер следа |
---|---|
1 000 000 | 64 Мб |
10 000 000 | 640 Мб |
100 000 000 | 6.4 Гб |
В настоящее время это основной метод существенного сокращения следа памяти экземпляра класса в памяти программы.
Достигается такое сокращение тем, что в памяти после заголовка объекта хранятся ссылки на объекты, а доступ к ним осуществляется при помощи специальных дескрипторов (descriptor), которые находятся в словаре класса:
>>> pprint(Point.__dict__) mappingproxy( . 'x': , 'y': , 'z': >)
Для автоматизации процесса создания класса со __slots__ существует библиотека namedlist. Функция namedlist.namedlist создает класс по структуре идентичный классу со __slots__ :
>>> Point = namedlist('Point', ('x', 'y', 'z'))
Другой пакет attrs позволяет автоматизировать процесс создания классов как со __slots__ , так и без него.
Tuple
Для представления наборов данных в Python также есть встроенный тип tuple . Tuple это фиксированная структура или запись, но без имен полей. Для доступа к полю используется индекс поля. Поля tuple раз и навсегда связываются с объектами-значениями в момент создания экземпляра tuple:
>>> ob = (1,2,3) >>> x = ob[0] >>> ob[1] = y # НЕВОЗМОЖНО
Экземпляры tuple вполне компактны:
>>> print(sys.getsizeof(ob)) 72
Они занимают в памяти на 8 байт больше, чем экземпляры классов со __slots__ , так как след tuple в памяти также содержит количеством полей:
Поле | Размер (байт) |
---|---|
PyGC_Head | 24 |
PyObject_HEAD | 16 |
ob_size | 8 |
[0] | 8 |
[1] | 8 |
[2] | 8 |
ВСЕГО: | 72 |
Namedtuple
Так как tuple используется очень широко, то однажды возник запрос на то, чтобы можно было все-таки иметь доступ к полям и по имени тоже. Ответом на этот запрос стал модуль collections.namedtuple .
Функция namedtuple создана, чтобы автоматизировать процесс порождения таких классов:
>>> Point = namedtuple('Point', ('x', 'y', 'z'))
Она создает подкласс tuple, в котором определены дескрипторы для доступа в полям по имени. Для нашего примера это будет выглядеть примерно так:
class Point(tuple): # @property def _get_x(self): return self[0] @property def _get_y(self): return self[1] @property def _get_y(self): return self[2] # def __new__(cls, x, y, z): return tuple.__new__(cls, (x, y, z))
Все экземпляры таких классов имеет след в памяти, идентичный с tuple. Большое число экземпляров оставляют чуть больший след памяти:
Количество экземпляров | Размер следа |
---|---|
1 000 000 | 72 Мб |
10 000 000 | 720 Мб |
Recordclass: мутируемый namedtuple без GC
Так как tuple и, соответственно, namedtuple -классы порождают немутируемые объекты в том смысле, что объект значение ob.x уже нельзя связать с другим объектом-значением, то возник запрос на мутируемый вариант namedtuple. Так как в Python нет встроенного типа, идентичного tuple, поддерживающего присваивания, то было создано множество вариантов. Мы остановимся на recordclass, получившем оценку на stackoverflow. Кроме того, с его помощью можно уменьшить размер следа объекта в памяти по сравнению с размером следа объектов типа tuple .
В пакете recordclass вводится в обиход тип recordclass.mutabletuple , который практически во всем идентичен tuple, но также поддерживает присваивания. На его основе создаются подклассы, которые практически во всем идентичны namedtuples, но также поддерживают присваивание новых значений полям (не создавая новых экземпляров). Функция recordclass подобно функции namedtuple позволяет автоматизировать создание таких классов:
>>> Point = recordclass('Point', ('x', 'y', 'z')) >>> ob = Point(1, 2, 3)
Экземпляры класса имеют аналогичную стуктуру, что и tuple , но только без PyGC_Head :
Поле | Размер (байт) |
---|---|
PyObject_HEAD | 16 |
ob_size | 8 |
x | 8 |
y | 8 |
y | 8 |
ВСЕГО: | 48 |
По умолчанию функция recordclass порождает класс, который не участвует в механизме циклической сборки мусора. Обычно namedtuple и recordclass используют для порождения классов, представляющих записи или простые (нерекурсивные) структуры данных. Корректное их использование в Python не порождает циклических ссылок. По этой причине в следе экземпляров классов, порожденных recordclass по умолчанию, исключен фрагмент PyGC_Head , который необходим для классов, поддерживающих механизм циклической сборки мусора (более точно: в структуре PyTypeObject , соответствующей создаваемому классу в поле flags по умолчанию не установлен флаг Py_TPFLAGS_HAVE_GC ).
Размер следа большого количества экземпляров оказывается меньше, чем у экземпляров класса со __slots__ :
Количество экземпляров | Размер следа |
---|---|
1 000 000 | 48 Мб |
10 000 000 | 480 Мб |
100 000 000 | 4.8 Гб |
Dataobject
Другое решение, предложенное в библиотеке recordclass основано на идее: использовать структуру хранения в памяти, как у экземпляров классов со __slots__ , но не участвовать при этом в механизме циклической сборки мусора. Класс порождается при помощи функции recordclass.make_dataclass :
>>> Point = make_dataclass('Point', ('x', 'y', 'z'))
Созданный таким образом класс по умолчанию, создает мутируемые экземпляры.
Другой способ – использовать объявление класса путем наследования от recordclass.dataobject :
class Point(dataobject): x:int y:int z:int
Создаваемые таким образом классы будут порождать экземпляры, которые не участвуют в механизме циклической сборки мусора. Структура экземпляра в памяти такая же, как в случае со __slots__ , но без заголовка PyGC_Head :
Поле | Размер (байт) |
---|---|
PyObject_HEAD | 16 |
x | 8 |
y | 8 |
y | 8 |
ВСЕГО: | 40 |
>>> ob = Point(1,2,3) >>> print(sys.getsizeof(ob)) 40
Для доступа к полям также используются специальные дескрипторы для доступа к полю по его смещению относительно начала объекта, которые размещены в словаре класса:
mappingproxy(, . 'x': , 'y': , 'z': >)
Размер следа большого количества экземпляров оказывается минимально возможным для CPython :
Количество экземпляров | Размер следа |
---|---|
1 000 000 | 40 Мб |
10 000 000 | 400 Мб |
100 000 000 | 4.0 Гб |
Cython
Есть один подход, основанный на использовании Cython. Его достоинство состоит в том, что поля могут принимать значения типов языка C. Дескрипторы для доступа к полям из чистого Python создаются автоматически. Например:
cdef class Python: cdef public int x, y, z def __init__(self, x, y, z): self.x = x self.y = y self.z = z
В этом случае экземпляры имеют еще меньший размер памяти:
>>> ob = Point(1,2,3) >>> print(sys.getsizeof(ob)) 32
След экземпляра в памяти имеет следующую структуру:
Поле | Размер (байт) |
---|---|
PyObject_HEAD | 16 |
x | 4 |
y | 4 |
y | 4 |
пусто | 4 |
ВСЕГО: | 32 |
Размер следа большого количества экземпляров получается меньше:
Количество экземпляров | Размер следа |
---|---|
1 000 000 | 32 Мб |
10 000 000 | 320 Мб |
100 000 000 | 3.2 Гб |
Однако следует помнить, что при доступе из кода на Python всякий раз будет осуществляться преобразование из int в объект Python и наоборот.
Numpy
Использование многомерных массивов или массивов записей для большого количества данных дает выигрыш в памяти. Однако для эффективной обработки на чистом Python следует использовать методы обработки, ориентированные на применение функций из пакета numpy .
>>> Point = numpy.dtype(('x', numpy.int32), ('y', numpy.int32), ('z', numpy.int32)])
Массив и N элементов, инициализированный нулями создается при помощи функции:
>>> points = numpy.zeros(N, dtype=Point)
Размер массива минимально возможный:
Количество экземпляров | Размер следа |
---|---|
1 000 000 | 12 Мб |
10 000 000 | 120 Мб |
100 000 000 | 1.20 Гб |
Обычный доступ к элементам массива и строкам потребует преобразования объекта Python
в значение C int и наоборот. Извлечение одной строки приводит к созданию массива, содержащего единственный элемент. След его уже не будет столь компактен:
>>> sys.getsizeof(points[0]) 68
Поэтому, как было отмечено выше, в коде на Python необходимо осуществлять обработку массивов, используя функции из пакета numpy .
Заключение
На наглядном и простом примере можно было убедиться, что сообщество разработчиков и пользователей языка программирования Python (CPython) располагает реальными возможностями для существенного сокращения объема памяти, используемой объектами.
Как установить Python на компьютер и начать на нём писать
Онлайн-компиляторы Python хороши, когда нужно быстро протестировать что-то простое, но для полноценной работы их недостаточно. Чтобы использовать всю мощь Python, нужно установить его на свой компьютер, и тогда можно подключать к нему любые библиотеки и писать код любой сложности.
В этой статье покажем, как установить Python под Windows и как с ним работать. Для MacOS всё почти то же самое, а если у вас Linux, то вы лучше нас знаете, как это сделать.
Скачивание и установка
Для начала нам нужно скачать установщик с официального сайта — python.org. Если качать Python с других сайтов, можно подцепить вирус или троян. Скачивайте программы только с официальных сайтов.
Несмотря на то, что Python 3 вышел 10 лет назад, до сих пор многие спорят про то, какую версию лучше использовать — вторую или третью. Мы за прогресс, поэтому качаем Python 3, но вы можете выбрать любую другую.
На главной странице сразу видим большую жёлтую кнопку, на которой написано «Download Python». Нажимаем, сохраняем и запускаем файл. Он весит около 25 мегабайт.
Когда установка закончится, нужно проверить, что всё было сделано правильно. Для этого в командной строке наберите py (латиницей) или python и нажмите клавишу ввода. Если всё хорошо, в ответ Python вам напишет номер своей версии и сборки и предложит несколько команд для знакомства с собой:
Запуск программ
Пока наш Python может работать только через командную строку — какие команды введёте, те он и выполнит. Многим разработчикам это нравится, но для старта это неудобно. Например, чтобы запустить программу, нужно написать в командной строке так:
Полное имя означает, что нужно написать не только название файла, но и диск с папкой, где он находится. Чтобы было понятнее, давайте возьмём наш код из статьи про таймер на Python и сохраним его в файле time.py3 на диске D. Py3 означает, что внутри этого файла будет код на Python3. Можно просто назвать файл python.py, без тройки, но для некоторых моментов это может быть важно.
Теперь, чтобы запустить наш код, напишем в командной строке:
Результат работы — выполненный алгоритм:
Подключаем VS Code
Мы уже рассказывали об этом редакторе кода — он быстрый, бесплатный и есть много плагинов. Чтобы работать с Python-файлами было проще, научим VS Code понимать и запускать их. Для этого нам нужно найти и установить специальный плагин для этого языка. Открываем вкладку Расширения (Extensions) и пишем такое:
В результатах поиска находим Python и нажимаем Install:
Затем то же самое делаем для подсветки синтаксиса: пишем в Расширениях команду ext:py3 и устанавливаем плагин MagicPython. После этого перезапускаем VS Code, открываем наш файл и нажимаем F5. Когда выпадающее окошко спросит, с чем будем работать — выбираем Python. В итоге мы получаем редактор кода с нужной нам подсветкой синтаксиса и результатом работы в том же окне. Красота!
Любишь Python? Зарабатывай на нём!
Изучите самый модный язык программирования и станьте крутым бэкенд-разработчиком. Старт — бесплатно.
Получите ИТ-профессию
В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.