Weakref (WeakKeyDictionary) для создания объектов (FrameType)

Я хочу иметь сопоставление dict с объектов активного фрейма (FrameType) с некоторыми данными. Активный означает, что он находится в текущей трассировке стека выполнения.

Однако хранить ссылку на объект фрейма — плохая идея, потому что это также будет держать неактивные фреймы и очень скоро взорвет память из-за всех ссылок на все локальные переменные.

Естественным решением было бы использовать слабую ссылку на объекты фрейма, а точнее WeakKeyDictionary.

Однако в настоящее время невозможно создать слабую ссылку на объект фрейма:

import weakref
import sys

f = sys._getframe()
weakref.ref(f)

урожаи

TypeError: cannot create weak reference to 'frame' object

Я предполагаю, что это деталь реализации CPython?

Итак, какие есть варианты?

Я мог бы в любом случае создать ссылки, но постараюсь очистить их как можно скорее, когда они больше не активны. Как на самом деле проверить, активен ли объект фрейма?

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
0
22
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вот сумасшедшая идея: если вы не можете хранить ссылку на фрейм, сделайте так, чтобы фрейм содержал ссылку на вас.

class FrameDict:
    def __init__(self):
        self._data_by_frame_id = {}
    
    def __setitem__(self, frame, data):
        frame_id = id(frame)
        
        # Make the frame hold a reference to a KeyDeleter, so when the frame
        # dies, the KeyDeleter dies, and the frame is removed from the dict
        deleter = KeyDeleter(self._data_by_frame_id, frame_id)
        frame.f_locals[deleter] = deleter

        self._data_by_frame_id[frame_id] = data
    
    def __getitem__(self, frame):
        frame_id = id(frame)
        return self._data_by_frame_id[frame_id]
        

class KeyDeleter:
    def __init__(self, mapping, key):
        self.mapping = mapping
        self.key = key
    
    def __del__(self):
        del self.mapping[self.key]

Демо:

import inspect

def new_frame():
    return inspect.currentframe()

frame_dict = FrameDict()

frame = new_frame()
frame_dict[frame] = 'foobar'
print(frame_dict[frame])  # foobar

del frame
print(frame_dict._data_by_frame_id)  # {}

Интересная идея. Но безопасно ли писать frame.f_locals? Другая проблема заключается в том, что id уникален только во время жизни объектов, поэтому ваш __getitem__ может возвращать неверные данные.

Albert 17.05.2022 10:56

@Albert f_locals — задокументированный атрибут; он указан в этот массивный стол. Я также не вижу проблемы с отображением на основе идентификатора, цепочка событий, которая должна вызвать ошибку, будет следующей: 1) кадр удаляется 2) новый кадр создается с идентификатором предыдущего кадра 3 ) вы ищете новый кадр в своем словаре 4) KeyDeleter.__del__ выполняется. Я не думаю, что есть реализация Python, в которой это может произойти.

Aran-Fey 17.05.2022 11:02

Ах, вы спросили, безопасно ли модифицировать f_locals. На самом деле это хороший вопрос. Я не уверен, но это работает ¯\_(ツ)_/¯

Aran-Fey 17.05.2022 11:17

Это может привести к сбою внутри функции — в глобальной области видимости locals и globals одинаковы, но в области действия функции locals представляет собой динамически построенное представление двух массивов фиксированного размера.

MisterMiyagi 17.05.2022 11:42

@MisterMiyagi Я использую локальные функции в своей демонстрации, все работает нормально. По крайней мере, в CPython.

Aran-Fey 17.05.2022 11:52

Цепочка «1) удаленный кадр 2) новый кадр с идентификатором предыдущего кадра 3) __getitem__ 4) KeyDeleter.__del__» может произойти с любым Python на основе GC, особенно CPython. Может быть, не так часто. Но запустите это 1 миллион раз, и вы, вероятно, увидите это.

Albert 17.05.2022 12:24

@Albert Хорошо, CPython также использует подсчет ссылок, а GC очищает только объекты, у которых есть цикл ссылок. Таким образом, вам сначала нужно создать эталонный цикл, содержащий кадр стека. И тогда вам каким-то образом придется вызывать FrameDict.__getitem__, пока сборщик мусора занимается очисткой этого рефцикла. Но если вы все еще беспокоитесь, вы можете уменьшить вероятность столкновения идентификаторов, сохранив дополнительные идентификаторы. Например, вместо использования id(frame) в качестве клавиши диктофона используйте (id(frame), id(frame.f_back), id(frame.f_locals)).

Aran-Fey 17.05.2022 12:36

Другие вопросы по теме