我有一个库,它在 WeakKeyDictionary 中存储外部用户对象的附加数据:
extra_stuff = weakref.WeakKeyDictionary()
def get_extra_stuff_for_obj(o):
return extra_stuff[o]
复制用户对象时,我希望副本具有相同的额外内容。但是,我对用户对象的控制有限。我想为将以这种方式使用的用户对象类定义一个类装饰器:
def has_extra_stuff(klass):
def copy_with_hook(self):
new = magic_goes_here(self)
extra_stuff[new] = extra_stuff[self]
klass.__copy__ = copy_with_hook
return klass
如果 klass 已经定义了 __copy__
,这很容易,因为我可以关闭 copy_with_hook
并调用它。但是,通常它没有定义。在这里叫什么?这显然不能是 copy.copy
,因为那样会导致无限递归。
我找到了 this question这似乎问了完全相同的问题,但实际上答案是错误的,因为这会导致深层复制,而不是副本。我也无法这样做,因为我需要为 deepcopy 和 copy 安装 Hook 。 (顺便说一下,我会继续讨论那个问题,但没有声誉我不能这样做。)
我查看了复制模块的作用,这是一堆涉及 __reduce_ex()
的巫术。我显然可以将它剪切/粘贴到我的代码中,或者直接调用它的私有(private)方法,但我认为这绝对是最后的手段。这看起来很简单,我确信我缺少一个简单的解决方案。
最佳答案
本质上,您需要 (A) 复制并保留原始 __copy__
(如果存在)(并委托(delegate)给它),否则 (B) 将 copy.copy
欺骗到 < strong>不使用您新添加的__copy__
(并委托(delegate)给copy,copy
)。
所以,例如...:
import copy
import threading
copylock = threading.RLock()
def has_extra_stuff(klass):
def simple_copy_with_hook(self):
with copylock:
new = original_copy(self)
extra_stuff[new] = extra_stuff[self]
def tricky_case(self):
with copylock:
try:
klass.__copy__ = None
new = copy.copy(self)
finally:
klass.__copy__ = tricky_case
extra_stuff[new] = extra_stuff[self]
original_copy = getattr(klass, '__copy__', None)
if original_copy is None:
klass.__copy__ = tricky_case
else:
klass.__copy__ = simple_copy_with_hook
return klass
这不是有史以来最优雅的代码,但至少它只是与 klass
一起玩,没有猴子补丁,也没有复制和粘贴 copy.py
本身: -)
补充:由于 OP 在评论中提到他不能使用此解决方案,因为该应用程序是多线程的,因此添加了适当的锁定以使其实际可用。使用单个全局可重入锁来确保不会由于在多个线程之间无序获取多个锁而导致死锁,并且可能过度锁定“以防万一”,尽管我怀疑简单的情况和棘手的字典分配case 可能不需要锁...但是,当线程受到威胁时,安全总比后悔好:-)
关于Python:如何安装传递复制/深度复制 Hook ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28051920/