Python - 对继承感到困惑

时间:2017-12-03 05:42:09

标签: python python-3.x

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

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

然后,第2轮,我取消注释self._abc = kwargs并评论其他3行, 答案变得错误。

为什么会这样?

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()

1 个答案:

答案 0 :(得分:3)

首先,为了说清楚,我想声明_abc是一个类变量,而不是一个实例变量,因此它的地址空间在父类和子类之间共享。 原始 _abc对象中的任何更改都会影响所有类_abc对象。阅读有关类和实例变量(here at SOhere at DO

的内容

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

  

id()返回对象的“标识”。这是一个整数(或长整数)   整数)保证对于该对象是唯一的和常量的   在其一生中。两个具有非重叠寿命的对象可以   具有相同的id()值。

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

In [33]: id(c._abc)
Out[33]: 4454841440 

In [34]: id(b._abc)
Out[34]: 4454841440

In [36]: id(A._abc)
Out[36]: 4454841440

In [38]: id(B._abc)
Out[38]: 4454841440

In [39]: id(C._abc)
Out[39]: 4454841440

它们都为id()提供相同的值。在第2轮中,当您取消注释self._abc = kwargs时,请查看id()的值:

In [8]: id(b._abc)
Out[8]: 4585625712 # its different from A._abc and c._abc why?

In [9]: id(c._abc)
Out[9]: 4585627152 # same as A._abc

In [10]: id(A._abc)
Out[10]: 4585627152 # this is same as c._abc

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

在第1轮你做的时候:

# self._abc = kwargs                 
self._abc['a'] = kwargs['a']
self._abc['b'] = kwargs['b']
self._abc['Hello'] = 'World'

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

self._abc = kwargs

此处,代码将kwargs(具有自己的地址空间的字典)分配给self._abcself._abc成为B类的实例变量,它只能用于B类的对象。

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

In [11]: class B(A):
    ...:
    ...:     def __init__(self,successor=None,**kwargs):
    ...:         self._successor = successor
    ...:         print(id(self._abc))
    ...:         print(id(kwargs))
    ...:         self._abc = kwargs
    ...:         print(id(self._abc))

In [12]: b = B(**list)
4585627152 # original self._abc id
4583538904 # kwargs object's id
4583538904 # New self._abc id

正如您最初看到的那样,self._abc的地址为4585627152,而kwargs的地址为4583538904,但self._abc= kwargs self._abc之后的新ID为4583538904。