Ответ 1
Вы можете сделать это, но все варианты беспорядочны и полны оговорок до почти бесполезности, поэтому сначала подумайте, действительно ли вы хотите.
Интернирование строки не продлевает срок ее службы. Вам не нужно беспокоиться о том, что интернированный диктует вечно, полный струн, которые вам не нужны. Таким образом, прерывание строк вряд ли будет актуальной проблемой памяти, и изучение того, сколько строк было интернировано, может быть довольно бесполезным.
Если вы все еще хотите это сделать, отпустите свои варианты.
Правильный путь, вероятно, должен был бы использовать вашу собственную интернирующую реализацию... за исключением того, что слабая поддержка ссылок Python не позволяет создавать слабые ссылки на строки. Это означает, что если вы попробуете этот подход, вы застряли либо в обходе своих слабых ссылочных оберток строк, либо сохраняете интернированные строки живыми навсегда. Оба варианта ужасны.
На самом деле есть функция, которая печатает информацию, о которой вы просите... но она также деинтерминирует все. Его существование является детальностью реализации, и оно доступно только через API C, поэтому нам нужно использовать ctypes.pythonapi
, чтобы получить его.
import ctypes
_Py_ReleaseInternedStrings = ctypes.pythonapi._Py_ReleaseInternedStrings
_Py_ReleaseInternedStrings.argtypes = ()
_Py_ReleaseInternedStrings.restype = None
_Py_ReleaseInternedStrings()
Вывод:
releasing 3461 interned strings
total size of all interned strings: 33685/0 mortal/immortal
Суммарными размерами являются суммы строк, поэтому они не включают заголовки объектов или нулевые терминаторы.
Вам, вероятно, не нравится, что нужно выпускать все интернированные строки каждый раз, когда вы хотите проверить, сколько их было. К сожалению, Python не раскрывает интернированного dict, даже через C API или через GC hooks. Что еще вы могли попробовать? Ну, перейдя к еще более сумасшедшим вариантам, там отладчик.
ecatmur отправил сумасшедший взлом, запустив процесс GDB в автоматическом режиме и используя условную точку останова, чтобы добраться до errnomap
, очень похоже на interned
dict, к которому вы хотите получить доступ. Это может быть адаптировано для доступа к interned
dict вместо этого. Это было бы очень не переносным и чрезвычайно сложно поддерживать.
Запуск отладчика также является ужасным вариантом. Что еще вы могли попробовать? Ну, вы всегда можете создать свою собственную сборку Python. Загрузите источник из python.org, добавьте
PyObject *
AwfulHackToGetTheInternedDict(void)
{
if (interned == NULL) {
// No interned dict yet.
Py_RETURN_NONE;
}
Py_INCREF(interned);
return interned;
}
до Objects/stringobject.c
, сборки и установки. Вероятно, вы захотите использовать virtualenv, чтобы сохранить это отдельно от вашего обычного интерпретатора Python. С помощью этого ужасного взлома вы можете сделать
import ctypes
AwfulHackToGetTheInternedDict = ctypes.pythonapi.AwfulHackToGetTheInternedDict
AwfulHackToGetTheInternedDict.argtypes = ()
AwfulHackToGetTheInternedDict = ctypes.py_object
interned = AwfulHackToGetTheInternedDict()
чтобы получить dict всех интернированных строк.
Итак, это ваши варианты или, по крайней мере, варианты, о которых я думал. Я также попытался заставить GC отслеживать строку, а затем интернировать ее, чтобы сделать интернированный dict видимым через GC, но вызов PyObject_GC_Track
в строке вызвал фатальную ошибку, так что это не сработает.