浅复制对象时如何重新初始化属性

问题描述 投票:0回答:1

我有这个简单的 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
属性,在复制时需要特殊处理。

我似乎找不到任何解决方案。有什么想法吗?

python copy uuid
1个回答
0
投票

我可能会尝试回答我自己的问题,但它确实提出了另一个问题...... 答案就在

__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__
,但我不确定我喜欢这个想法。

© www.soinside.com 2019 - 2024. All rights reserved.