python命名空间中的名称分配和重用



我目前正试图弄清楚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仍然持有ref。

至于我的问题:这是故意的行为吗?如果是,背后的原因是什么?对我来说,这看起来有点太含蓄了。我本以为这会失败并抛出一些异常,tbh。

编辑

为了解释为什么这让我印象深刻,并在下面的评论中回答Daniel Roseman:

a1和a2都认为自己是__main__.A的实例,而实际上其中一个是曾经的__main__.A的实例。这给我留下了一个活动的、可用的对象,它在我的命名空间中没有明显定义的句柄。我认为这是类对象独有的,因为它们具有类对象的对偶性。

我正在开发一些工具,它可以动态地构建类,并在它们的实例上执行命令序列,所有这些都基于一些奇怪的输入。这涉及到许多动态进口。在不了解这种行为的情况下,当我真正应该研究一些线程问题时,我可能会调试类及其构建过程。

这是预期行为吗

是的。

背后的原因是什么

正如Daniel Roseman在上面的评论中所说的那样,这是Python中的所有东西都是对象这一一般规则的一个具体例子。

考虑以下代码片段:

some_string_a = 'white'
some_string_b = 'black'
a = some_string_a
print(a)
>>> white
a = some_string_b
print(a)
>>> black
print(some_string_a)
>>> white

我相信您不会对仍然可以与some_string_a交互感到惊讶,即使以前引用它的变量名已经被重新分配,现在引用了不同的东西。

但是,由于问题中的两个类也是对象,因此情况类似。为什么不能仅仅因为重新分配了变量名(A)而与对象(您定义的第一个类)交互,使其现在引用不同的对象?

正如你在编辑中提到的,这可能会导致一些名称空间的怪异。。。但这就是Python通过将所有东西都视为对象来获得一致性所付出的代价。

最新更新