我有一个有很多成员的大班,并且有很多关于这个类的实例的引用。不幸的是(出于合理的原因)所有这些引用都是错误的方法。
我没有重新创建每个(并且在任何地方查找和更新对象),或者每次访问此类时添加额外的间接级别,或者单独交换成员,我都定义了一个方法:
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__
),但感觉可能有一个我不应该这样做的原因,是吗?
这是我的实际用例:
我有一种以(必然)昂贵的方式实现比较运算符的类型。我有各种包含此类对象的排序数据结构。
当我对其中一个对象执行某些操作时,我知道比较顺序已更改,并且我的数据结构中的顺序(所有这些顺序!)都可以通过将修改后的对象与“下一个更大”的对象进行交换来恢复
答案 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__
的值,并使用您选择的交换算法。它更冗长,而且效率可能更低,但是如果你在不调试时节省的时间比在运行时节省的时间多,那么它可能值得你花时间。