我有这个简单的 mixin,可以为任何新实例分配一个新的 uuid:
import uuid
import copy
class UuidMixin:
def __new__(cls):
obj = super().__new__(cls)
obj.uuid = uuid.uuid4()
return obj
class Foo(UuidMixin):
pass
f = Foo()
一切正常,直到我决定浅复制实例
f
:
f2 = copy.copy(f)
f.uuid == f2.uuid # is True :(
但我希望
f2
有自己的 uuid,因为它是一个新对象。
最明显的方法是定义一个
__copy__
方法,例如
class UuidMixin:
def __new__(cls):
obj = super().__new__(cls)
obj.uuid = uuid.uuid4()
return obj
def __copy__(self):
obj = self.__class__.__new__(self.__class__)
# I *could* return `obj` here if the class hadn't any attr but `uuid`
d = copy.copy(self.__dict__)
d.pop('uuid')
self.__dict__.update(d)
return obj
这可行,但似乎不是一个可靠的解决方案:如果我需要在继承树上进一步定义
Foo.__copy__
,甚至另一个 __copy__
方法怎么办?在所有情况下,我都需要记住有一个特殊的 uuid
属性,在复制时需要特殊处理。
我似乎找不到任何解决方案。有什么想法吗?
我可能会尝试回答我自己的问题,但它确实提出了另一个问题...... 答案就在
__getstate__
方法中:
class UuidMixin:
def __new__(cls):
obj = super().__new__(cls)
obj.uuid = uuid.uuid4()
return obj
def __getstate__(self):
state = self.__dict__.copy()
del state["uuid"]
return state
这符合预期。或多或少:我们会遇到问题,例如涉及多重继承。
但新的主要问题是酸洗:酸洗
__getstate__
的实例时会调用相同的方法 Foo
。所需的行为是:不要浅复制 uuid,而是对其进行 pickle/unpickle(此解决方案不会发生这种情况)。
据我所知,复制协议和pickling协议是一样的。这不是违反了单一职责原则吗?
pickle文档说:
正如我们将看到的,pickle 并不直接使用所描述的方法 上面[
,__getstate__
]。其实这些方法都是 复制协议的一部分,实现了__setstate__
特殊 方法。__reduce__()
但我找不到任何进一步的解释。
您对如何解耦复制和pickle协议有什么想法吗?我通过从内部检查调用堆栈取得了一些成功
__getstate__
,但我不确定我喜欢这个想法。