Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python - Confused about inheritance

I write the test code with 3 classes , and using Chain of Responsibility design pattern , the code below

and I print print(c._abc is b._abc), the answer is True , but my original think is that the two are different.

Then , Round 2 , I uncomment self._abc = kwargs and comment other 3 lines , the answer become False .

Why is it so?

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()
like image 394
Relax ZeroC Avatar asked Dec 03 '17 05:12

Relax ZeroC


People also ask

What is true about inheritance in Python?

Inheritance allows us to define a class that inherits all the methods and properties from another class. Parent class is the class being inherited from, also called base class. Child class is the class that inherits from another class, also called derived class.

Why inheritance is not working in Python?

You need to explicitly call the constructor. It isn't called for you automatically like in C++ Use a new-style class inherited from object. With a new-style class, use the super() method available.

How does Python detect inheritance?

Two built-in functions isinstance() and issubclass() are used to check inheritances. The function isinstance() returns True if the object is an instance of the class or other classes derived from it. Each and every class in Python inherits from the base class object .


1 Answers

First just to make it clear I would like to state that _abc is a class variable, not an instance variable so its address space is shared across parent and child class. Any change in the original _abc object affects all the class's _abc object. Read about class and instance variable (here at SO and here at DO)

To check that _abc share the same address space we can use python built-in id():

id() Return the “identity” of an object. This is an integer (or long integer) which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value.

To check this in the first round we can do:

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

All of them gives the same value for id(). In round 2, when you uncomment self._abc = kwargs see what happens to the value of 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

Value of b._abc gets changed but the value of c._abc and A._abc remains the same. So, what is actually happening here?

When in round 1 you do:

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

You are actually modifying the shared class variable _abc. Your code here is not creating a new self._abc object it is modifying the original self._abc class variable object but in round 2 when you do:

self._abc = kwargs

Here, the code is assigning kwargs, which is a dictionary with its own address space, to self._abc. self._abc becomes class B's instance variable and it is only available to the class B's objects.

To verify this point you can modify your class B to print id's as:

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

As you can see originally self._abc had address 4585627152 and kwargs had 4583538904 but after self._abc= kwargs self._abc's new id is 4583538904.

like image 70
Amit Tripathi Avatar answered Nov 15 '22 08:11

Amit Tripathi