Внутри питона есть ЕЩЕ виртуальные машины Мы все знаем, что сам питон… — Находки в опенсорсе — TG.ME

Внутри питона есть ЕЩЕ виртуальные машины

Мы все знаем, что сам питон - одна большая стековая виртуальная машина, которая выполняет опкоды. Их мы можем посмотреть через dis:


>>> import dis
>>> dis.dis('x + y')
0 RESUME 0

1 LOAD_NAME 0 (x)
LOAD_NAME 1 (y)
BINARY_OP 0 (+)
RETURN_VALUE


Можем получить список всех опкодов, можем вызвать их оптимизации и посмотреть на результат с оптимизациями.

Но! Внутри CPython есть и другие виртуальные машины. Сегодня поговорим про ту, которой все мы всегда пользовались, но не знали, что она - виртуальная машина.

pickle

Да, не удивляйтесь. Встроенный протокол сериализации в питоне работает благодаря отдельной стековой виртуальной машине. Давайте посмотрим.


>>> class User:
... def __init__(self, username: str, tags: list[str]) -> None:
... self.username = username
... self.tags = tags
... def __reduce__(self) -> tuple[type['User'], tuple[Any, ...]]:
... return (type(self), (self.username, self.tags))


Создадим обычный класс и запиклим его объект:


>>> import pickle
>>> user = User('sobolevn', tags=['python', 'tg'])
>>> pickle.dumps(user, protocol=0)
b'c__main__\nUser\np0\n(Vsobolevn\np1\n(lp2\nVpython\np3\naVtg\np4\natp5\nRp6\n.'


Обратите внимание, что в разных протоколах значение будет разное:


>>> pickle.dumps(user, protocol=1)
b'c__main__\nUser\nq\x00(X\x08\x00\x00\x00sobolevnq\x01]q\x02(X\x06\x00\x00\x00pythonq\x03X\x02\x00\x00\x00tgq\x04etq\x05Rq\x06.'


Всегда необходимо тестировать, что pickle работает для всех версий от 0 до pickle.HIGHEST_PROTOCOL для ваших объектов, которые поддерживают такой способ сериализации.

Что внутри?

Можно, глядя на значения, подумать, что там просто лежит какой-то бинарный формат сериалиации. Однако, там лежат опкоды виртуальной машины для сериалиации объектов. Их можно задисить:


>>> import pickletools
>>> pickletools.dis(pickle.dumps(user, protocol=1))
0: c GLOBAL '__main__ User'
15: q BINPUT 0
17: ( MARK
18: X BINUNICODE 'sobolevn'
31: q BINPUT 1
33: ] EMPTY_LIST
34: q BINPUT 2
36: ( MARK
37: X BINUNICODE 'python'
48: q BINPUT 3
50: X BINUNICODE 'tg'
57: q BINPUT 4
59: e APPENDS (MARK at 36)
60: t TUPLE (MARK at 17)
61: q BINPUT 5
63: R REDUCE
64: q BINPUT 6
66: . STOP
highest protocol among opcodes = 1


Сравните, как будет отличаться вывод для другого протокола, например пятого.

И окажется, что все "случайные" символы на самом деле просто так же обозначают опкоды. Теперь мы умеем их читать.

Мы можем найти все опкоды и посмотреть их доки:


>>> pickletools.opcodes[25].code
']'
>>> pickletools.opcodes[25].doc
'Push an empty list.'


И мы даже можем оптимизировать байткод pickle для более быстрой сериализации / десериализации.
Прям полностью настоящая ВМ :)

Вот за счет чего мы можем с помощью pickle сериализовать любой Python объект (почти), а с помощью других средств - получается сильно сложнее.

Обсуждение: Знали о такой детали реализации? Знаете ли вы как работает pickle сам по себе? Зачем нужны протоколы и версии? Или сделать отдельный пост про детали работы? Знаете ли вы, что pickle - фундаментально небезопасный протокол? И нельзя запускать чужие дампы, только свои доверенные?

Загадка: кстати, какие еще виртуальные машины внутри CPython вы знаете? Я назвал только одну из нескольких. Заходите в комменты за ответами, правильные - покажу завтра.
Python documentation
pickletools — Tools for pickle developers
Source code: Lib/pickletools.py This module contains various constants relating to the intimate details of the pickle module, some lengthy comments about the implementation, and a few useful functi...
3👍91🔥33❤17🤯3😢2
July 13, 2026 9.6K 97 62