Python - 对继承感到困惑

Rel*_*roC 6 python python-3.x

我用3个类编写测试代码,并使用Chain of Responsibility设计模式,下面的代码

我打印print(c._abc is b._abc),答案是真的,但我原来认为两者是不同的.

然后,在第2轮,我取消注释self._abc = kwargs并评论其他3行,答案变为False.

为什么会这样?

import abc

class A:
    __metaclass__ = abc.ABCMeta

    _abc = {}

    def __init__(self,successor=None,**kwargs):
        self._successor = successor

    @abc.abstractmethod
    def handlerRequest(self):
        pass

class B(A):

    def __init__(self,successor=None,**kwargs):
        self._successor = successor
        print(kwargs)
        # self._abc = kwargs                 # round 2<---uncomment here
        self._abc['a'] = kwargs['a']         # round 2<---comment here
        self._abc['b'] = kwargs['b']         # round 2<---comment here
        self._abc['Hello'] = 'World'         # round 2<---comment here

    def handlerRequest(self):
        if (self._successor is not None):
            self._successor.handlerRequest()

        print(self._abc)

class C(A):

    def handlerRequest(self):
        if (self._successor is not None):
            self._successor.handlerRequest()
        print(self._abc)

list = {'a':1,'b':2}
b = B(**list)
c = C(b)
print(c._abc is b._abc)
c.handlerRequest()
Run Code Online (Sandbox Code Playgroud)

Ami*_*thi 3

首先,为了明确起见,我想声明这_abc是一个类变量,而不是实例变量,因此它的地址空间在父类和子类之间共享。原始 对象中的任何更改_abc都会影响该类的所有_abc对象。阅读有关类和实例变量的信息(此处位于 SO此处位于 DO

\n\n

要检查_abc共享相同的地址空间,我们可以使用 python 内置id()

\n\n
\n

id() 返回对象的\xe2\x80\x9cidentity\xe2\x80\x9d。这是一个整数(或 long\n 整数),保证该对象在其生命周期内是唯一且恒定的。具有不重叠生命周期的两个对象可能具有相同的 id() 值。

\n
\n\n

要在第一轮中检查这一点,我们可以这样做:

\n\n
In [33]: id(c._abc)\nOut[33]: 4454841440 \n\nIn [34]: id(b._abc)\nOut[34]: 4454841440\n\nIn [36]: id(A._abc)\nOut[36]: 4454841440\n\nIn [38]: id(B._abc)\nOut[38]: 4454841440\n\nIn [39]: id(C._abc)\nOut[39]: 4454841440\n
Run Code Online (Sandbox Code Playgroud)\n\n

它们都为 id() 提供相同的值。在第 2 轮中,当您取消注释时self._abc = kwargs看看 id() 的值会发生什么情况:

\n\n
In [8]: id(b._abc)\nOut[8]: 4585625712 # its different from A._abc and c._abc why?\n\nIn [9]: id(c._abc)\nOut[9]: 4585627152 # same as A._abc\n\nIn [10]: id(A._abc)\nOut[10]: 4585627152 # this is same as c._abc\n
Run Code Online (Sandbox Code Playgroud)\n\n

b._abc 的值发生更改,但 c._abc 和 A._abc 的值保持不变。那么,这里到底发生了什么?

\n\n

在第 1 轮中,您执行以下操作:

\n\n
# self._abc = kwargs                 \nself._abc[\'a\'] = kwargs[\'a\']\nself._abc[\'b\'] = kwargs[\'b\']\nself._abc[\'Hello\'] = \'World\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

您实际上正在修改共享类变量_abc。这里的代码不是创建一个新self._abc对象,而是修改原始对象self._abc类变量对象,但在第 2 轮中,当您这样做时:

\n\n
self._abc = kwargs\n
Run Code Online (Sandbox Code Playgroud)\n\n

此处,代码将kwargs即具有自己的地址空间的字典,分配给self._abcself._abc成为类 B 的实例变量,并且仅可用于类 B 的对象。

\n\n

要验证这一点,您可以修改 B 类以将 id 打印为:

\n\n
In [11]: class B(A):\n    ...:\n    ...:     def __init__(self,successor=None,**kwargs):\n    ...:         self._successor = successor\n    ...:         print(id(self._abc))\n    ...:         print(id(kwargs))\n    ...:         self._abc = kwargs\n    ...:         print(id(self._abc))\n\nIn [12]: b = B(**list)\n4585627152 # original self._abc id\n4583538904 # kwargs object\'s id\n4583538904 # New self._abc id\n
Run Code Online (Sandbox Code Playgroud)\n\n

正如你所看到的,最初 self._abc 有地址4585627152,kwargs 有4583538904,但self._abc= kwargs self._abc\ 的新 id 是 4583538904。

\n