TGViewer
Moscow Python Moscow Python @moscow_python · 3.03K subscribers
Post #910 1.05K

Forwarded from Находки в опенсорсе

Три типа объектов в Питоне

В питоне часто любят обсуждать "мутабельные" и "иммутабельные" объекты, но крайне редко объясняют, в чем же на самом деле разница. Сегодня мы посмотрим на такое со стороны C.

PyObject

Все мы знаем, что в питоне все объект или PyObject *, который упрощенно выглядит так (в FT сборке он посложнее):


struct _object {
Py_ssize_t ob_refcnt;
PyTypeObject *ob_type;
}


То есть: у нас есть только счетчик ссылок на объект и указатель на его тип. Первое меняется очень часто, если объект не immortal. Второе тоже можно менять в некоторых случаях:


>>> class A:
... __slots__ = ()

>>> class B:
... __slots__ = ()

>>> a = A()
>>> type(a)
<class '__main__.A'>
>>> a.__class__ = B
>>> type(a)
<class '__main__.B'>


Получается, что большинство объектов мутабельные уже на данном уровне.

Но, в целом есть три типа объектов, разные по уровню мутабельности:
1. Такие как None: ob_refcnt не меняется (immortal), тип менять нельзя, ведь Py_TPFLAGS_IMMUTABLETYPE установлен (static type), размер неизменный 0 для всех потенциальных значений
2. Такие как int: ob_refcnt может меняться для больших чисел (маленькие инты - immortal), тип менять нельзя, размер нельзя менять, но он будет разный для разных чисел:


>>> sys.getsizeof(1)
28
>>> sys.getsizeof(10000000000000)
32


3. Такие как list: ob_refcnt всегда меняется, тип менять нельзя, размер меняется

Отдельно нужно отметить, что пользовательские классы обычно еще более мутабельны, потому что и тип менять можно.
Но, вопрос в другом: а где вообще хранится размер объекта и его внутренности? Раз в PyObject ничего такого нет.

C-API

В C-API питона есть два полезных макроса: PyObject_HEAD для объектов фиксированного размера и PyObject_VAR_HEAD для объектов, которые могут менять размер.


struct PyVarObject {
PyObject ob_base;
Py_ssize_t ob_size; /* Number of items in variable part */
};

#define PyObject_HEAD PyObject ob_base;
#define PyObject_VAR_HEAD PyVarObject ob_base;


Хотим поменять размер объекта? Увеличиваем ob_size, аллоцируем новую память (если нужно) под новые объекты внутри.

Итоговые объекты используют примерно такую логику:


typedef struct {
PyObject_VAR_HEAD
/* Vector of pointers to list elements. list[0] is ob_item[0], etc. */
PyObject **ob_item;

/* ob_item contains space for 'allocated' elements. The number
* currently in use is ob_size.
*/
Py_ssize_t allocated;
} PyListObject;


То есть: на самом деле все объекты list (и любые другие) не просто PyObject, они:
- Имеют свой собственный тип: PyListObject
- Имеют общую абстракцию для размерности: PyVarObject
- Имеют общую абстракцию для типа и счетчика ссылок: PyObject

Я сделал небольшой очень упрощенный пример. Там я показываю в том числе, как происходит каст одного типа поинтера в другой в C.

Итог

1. None не имеет внутреннего состояния вообще (не использует ничего)
2. int может иметь разный размер, но не может изменяться, потому использует PyObject_HEAD (раньше был PyObject_VAR_HEAD, там сложная история):


typedef struct _PyLongValue {
uintptr_t lv_tag; /* Number of digits, sign and flags */
digit ob_digit[1];
} _PyLongValue;

struct _longobject {
PyObject_HEAD
_PyLongValue long_value;
};


3. list может иметь разный размер и может изменяться, потому использует PyObject_VAR_HEAD, как я показывал выше

Обсуждение: как вы думаете, как работает len() для list?

| Поддержать | YouTube | GitHub | Чат |
Python documentation Common Object Structures There are a large number of structures which are used in the definition of object types for Python. This section describes these structures and how they are used. Base object types and macros: All ...
  • ❤ 11
  • 🤓 1
  • 💊 1
More from @moscow_python
  1. Sep 22, 2026Скоро начинаем! Трансляция - на https://moscowpython.ru
  2. Sep 22, 2026Напоминаем, что сегодня вечером состоится наш митап в Центральном университете. Зарегистри…
  3. Sep 21, 2026Важное прибавление к кругу резидентов Moscow Python Pro - с нами Николай Хитров, тимлид в…
  4. Sep 18, 2026С днем рождения🎉 Сегодня, ровно 3 года назад, 18 сентября 2023 года, вышла первая версия…
  5. Sep 18, 2026Напоминаем, что уже на следующей неделе - 22-го сентября в Центральном университете состои…
  6. Sep 15, 2026Друзья, у нас важное объявление! Некоторые из вас помнят наши курсы Learn Python - когда-т…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →