我可以通过交换__dict__来有效地交换两个类实例吗?


问题内容

我有一个很大的类,成员很多,周围有很多关于此类实例的参考。不幸的是(出于合理的原因)所有这些参考都是错误的方法。

我定义了一个方法,而不是重新创建每个对象(并在引用对象的任何地方查找和更新),或者每次我访问此类或单独交换成员时都添加额外的间接级别,而不是:

def swap(self, other):
    assert(isinstance(other, self.__class__))
    self.__dict__, other.__dict__ = other.__dict__, self.__dict__

所以我可以做:

instance_a.swap(instance_b)
# now all references to instance_a everywhere are as if instance_a is instance_b

问题: 似乎工作正常(我不使用__slots__),但感觉可能是我不应该这样做的原因,在吗?


这是我的实际用例:

我有一种类型,它以(必需)昂贵的方式实现比较运算符。我有各种排序的数据结构,其中包含这种类型的对象。

当我对其中一个对象执行操作时,我知道比较顺序已更改,并且可以通过将修改后的对象与“下一个更大的”对象交换来恢复数据结构中的顺序(所有这些!)。


问题答案:

如果我理解正确,那么您实际上是在交换一个类的实例,而不是类。

实例状态保存在两个可能的位置: __slots__和中__dict__。如果交换它们,则基本上交换了实例,同时保留了原始名称绑定。一个警告是,该类
不能 是不变的(必须不定义__hash__()),因为任何已经是集合成员或字典中键的实例都将变得不可检索。

如果是我,我想我应该将.swap()方法改为一个类方法-我认为它将更容易阅读:

class SomeBigClass():
    @staticmethod
    def swap_dict(instance_a, instance_b):
        instance_a.__dict__, instance_b.__dict__ = \
                instance_b.__dict__, instance_a.__dict__
    @classmethod
    def swap_slot(cls, instance_a, instance_b):
        values = []
        for attr in cls.__slots__:
            values.append(
                (attr,
                 getattr(instance_a, attr),
                 getattr(instance_b, attr),
                 ))
        for attr, val1, val2 in values:
            setattr(instance_a, attr, val2)
            setattr(instance_b, attr, val1)

然后再

SomeBigClass.swap_dict(this_instance, other_instance)

要么

SomeBigClass.swap_slot(this_instance, other_instance)

你为什么不这样做呢?如果您有实例绑定到名称,则不应这样做。考虑:

frubbah = SomeBigClass(attr="Hi, Mom!")
zikroid = SomeBigClass(attr='Hi, Dad!")
SomeBigClass.swap_dict(frubbah, zikroid)

交换之后,您认为自己对zikroid所了解的一切可能都已改变。