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

时间:2011-08-31 10:38:45

标签: python

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

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

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__),但感觉可能有一个我不应该这样做的原因,是吗?


这是我的实际用例:

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

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

4 个答案:

答案 0 :(得分: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'

答案 1 :(得分: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)

然后再

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所知的一切都可能发生了变化。

答案 2 :(得分:1)

此交换应在已排序的数据结构中实现,而不是在此类的实例中实现。

答案 3 :(得分:0)

为了您自己的理智,以及任何可能在您之后使用您的代码的人,如果您决定使用此功能,您应该记录下来,以便您知道发生了什么。当然,更自我文档化的替代方法是编写一个单独交换所有数据成员的函数。显然,如果您的数据模型将发生变化,这不是一个面向未来的证据,但它不会让您感到困惑。如果您不想手动写出来,可以定义一个函数来交换一个数据字段,并在所有类属性上运行它:

def swap (a, b):
  for key in a.__dict__.keys():
    swapOneField(key, a, b)

swapOneField获取a.__dict__b.__dict__的值,并使用您选择的交换算法。它更冗长,而且效率可能更低,但是如果你在不调试时节省的时间比在运行时节省的时间多,那么它可能值得你花时间。