如何使用 weakref/gc 支持实习对象?

How to intern objects with weakref/gc support?

提问人:Yakov Galka 提问时间:5/30/2023 更新时间:5/31/2023 访问量:60

问:

我正在尝试在 python 中实现非字符串对象的实习。对于字符串,我们有函数。但是,它不支持其他不可变对象。为了不碍事,我知道修改被拘禁对象时可能出现的问题。sys.intern

一个经常被引用的非字符串实习方式是:

S = {}
x = S.setdefault(x, x)

然而,这样的对被拘禁的对象有很强的引用,因此会无限增长,这是我的应用程序中的一个问题。dict

我发现有一种类型可以自动收集未引用的元素。但是,似乎没有一种快速的方法可以在集合中获取持有的对象:WeakSet

S = WeakSet()
S.add(x)    # returns nothing

# a very slow way to work around it:
for y in S:
    if y == x:
        x = y

还有 和 ,但似乎没有 .WeakKeyDictionaryWeakValueDictionaryWeakKeyValueDictionary

那么如何在python中实现这一点呢?

python 圾回收 弱引用

评论


答:

1赞 blhsing 5/31/2023 #1

您可以通过创建对对象的弱引用作为键,然后将对象存储为默认值来模拟:WeakKeyValueDictionaryWeakValueDictionary

from weakref import ref, WeakValueDictionary

def intern(obj, weak_refs=WeakValueDictionary()):
    return weak_refs.setdefault(ref(obj), obj)

因此:

import gc

a = frozenset(['x'*10000])
b = frozenset(['x'*10000])
assert a is not b
a = intern(a)
b = intern(b)
assert a is b
print(len(intern.__defaults__[0]))
del a
del b
gc.collect()
print(len(intern.__defaults__[0]))

传递断言和输出:

1
0

演示:https://replit.com/@blhsing/UsableSpecificProcessors

评论

0赞 user2357112 5/31/2023
嗯。。。是的,在这种情况下,当假设的 WeakKeyValueDictionary 的键和值是同一个对象时,只需自己在一侧创建 weakrefs 就可以了。当键和值是不同的对象时,它不会 - 你会遇到清理未正确进行的问题,或者清理未与内部迭代防护正确交互。
0赞 user2357112 5/31/2023
只使用普通函数而不是带有重写的类会更简单。这并不是说你想要一个实际的实例。__new__Intern
0赞 blhsing 5/31/2023
我把它做了一个类,这样我就可以更容易地输出弱容器的内容,但是是的,如果我把它做成一个函数,把弱容器作为一个参数的默认值,我仍然可以使用这个属性来访问弱容器,只是以一种稍微丑一点的方式,但没关系,因为丑只在调试代码中, 而实际代码在作为函数编写时确实更干净。__defaults__
0赞 blhsing 5/31/2023
键和值始终同步到此应用程序的相同对象,因此不会出现任何清理问题。
0赞 Yakov Galka 6/1/2023
有什么理由让 WeakValueDictionary 带有 ref() 键,而 WeakKeyDictionary 带有 ref() 值?