我目前正在研究Python中的mappingproxies和命名空间内部。在测试时,我遇到了一些意料之外的行为。
我定义了一个类,实例化它,然后在同样的名称下定义另一个类并实例化它。
class A:
color = 'white'
def __init__(self, val):
self.val = val
a1 = A(1)
class A:
color = 'black'
def __init__(self, val):
self.val = val
a2 = A(2)
现在,a1和a2是两个不同类的实例对象,它们都仍然存在。在命名空间中,第二个被修改过的类版本现在被赋给了名称A。而第一个最初的类对象只能通过其实例的__class__
属性来访问。尽管如此,您仍然可以完全与旧的类对象进行交互。
print(a1.__class__ is a2.__class__) # False
print(a1.__class__ is A) # False
print(a2.__class__ is A) # True
a3 = a1.__class__(3)
print(a3.__class__ is a1.__class__) # True
print(a3.color) # white
a3.__class__.color = 'red'
print(a1.color) # red
我猜想Python的对象引用计数器是导致旧类对象仍然存在的原因,因为当新的类对象被分配给名称A时,计数器并不为零,因为a1仍然保持一个引用。至于我的问题:这是预期的行为吗?如果是这样,背后的原因是什么?对我来说,这看起来有点太隐晦了。老实说,我希望它失败并抛出一些异常。
编辑
为了解释为什么这让我感到震惊,并回答下面评论中的Daniel Roseman的问题:
a1和a2都认为自己是__main__.A
的实例,而实际上它们中的一个是曾经的__main__.A
的实例。这使我拥有了一个活动且可用的对象,在我的命名空间中没有明确定义的句柄。我认为这是由于类对象具有类对象二元性质所特有的问题。
我正在开发一些基于奇怪输入动态构建类并在其实例上执行命令序列的工具。这涉及到很多动态导入。不知道这种行为,我可能会结束调试类及其构建过程,而实际上我应该查看一些线程问题。