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

Jam*_*mes 15 python

我有一个有很多成员的大班,并且有很多关于这个类的实例的引用.不幸的是(出于合理的原因)所有这些引用都是错误的方法.

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

def swap(self, other):
    assert(isinstance(other, self.__class__))
    self.__dict__, other.__dict__ = other.__dict__, self.__dict__
Run Code Online (Sandbox Code Playgroud)

所以我可以这样做:

instance_a.swap(instance_b)
# now all references to instance_a everywhere are as if instance_a is instance_b
Run Code Online (Sandbox Code Playgroud)

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


这是我的实际用例:

我有一种以(必然)昂贵的方式实现比较运算符的类型.我有各种包含此类对象的排序数据结构.

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

cwa*_*ole 9

编辑

你正在做的事情是可能的,虽然它会让人感到畏缩,因为它是黑客.如果可能的话,我建议您考虑重写/重构您的比较运算符.到目前为止,这将给你最好的结果.当然,不知道所涉及的范围或时间范围,很难说这是否立即可行,但相信我,如果你能做"正确"的事情,你将花更少的时间重写长期.

原版的

实际上,听起来你正在处理三个类 - 一个数据对象和两个实用程序类 - 但这是另一个问题.

这会打破,所以我要继续说"不,你不能通过交换__dict__s 来交换类":

>>> class Foo:
...    def __init__(self):
...        self.__bar = 1
...    def printBar(self):
...        print self.__bar
... 
>>> class Bar:
...    def __init__(self):
...        self.__bar=2
...    def printBar(self):
...        print self.__bar
... 
>>> f=Foo()
>>> f.printBar() # works as expected
1
>>> f=Foo()
>>> b=Bar()
>>> f.__dict__, b.__dict__ = b.__dict__, f.__dict__
>>> f.printBar() # attempts to access private value from another class
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in printBar
AttributeError: Foo instance has no attribute '_Foo__bar'
Run Code Online (Sandbox Code Playgroud)


Eth*_*man 6

如果我理解正确,你实际上是在交换类的实例,而不是类.

实例状态保存在两个可能的位置: __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)
Run Code Online (Sandbox Code Playgroud)

然后是

SomeBigClass.swap_dict(this_instance, other_instance)
Run Code Online (Sandbox Code Playgroud)

要么

SomeBigClass.swap_slot(this_instance, other_instance)
Run Code Online (Sandbox Code Playgroud)

你为什么不这样做?如果您有绑定名称的实例,则不应执行此操作.考虑:

frubbah = SomeBigClass(attr="Hi, Mom!")
zikroid = SomeBigClass(attr='Hi, Dad!")
SomeBigClass.swap_dict(frubbah, zikroid)
Run Code Online (Sandbox Code Playgroud)

在交换之后,你认为你知道zikroid的一切可能都发生了变化.