[地雷] 同名類別,且具有繼承關係,但位於不同模組下 - tsungjung411/python-study GitHub Wiki

檔案1:<your_app_path>/folder1/a.py

class A:
    def __init__(self):
        self.__name = 'A'
        self.__age = 18
    # end-of-def
# end-of-class

檔案2:<your_app_path>/folder2/a.py

from folder1.a import A as A1

class A(A1): # AA
    def __init__(self):
        self.__name = 'AA'
        print('> @AA: __name:', self.__name)
        super(A, self).__init__()
        print('< @AA: __name:', self.__name)
        
        try:
            print('< @AA: __age:', self.__age)
        except Exception as e:
            print(e)
            print('< @AA: cannot access self.__age:')
        # end-of-try
        
        try:
            print('< @AA: __real_age:', self.__real_age)
        except Exception as e:
            print(e)
            print('< @AA: cannot access self.__real_age:')
        # end-of-try
    # end-of-def
    
    def __setattr__(self, name, value):
        print('  - @AA: __setattr__: name:', name)
        print('  - @AA: __setattr__: value:', value)
        super(A, self).__setattr__(name, value)
    # end-of-def
# end-of-class

檔案3:<your_app_path>/main.py

from folder2.a import A as A2

a2 = A2()

執行 main.py 的結果:

$ python3 main.py 

  - @AA: __setattr__: name: _A__name
  - @AA: __setattr__: value: AA
> @AA: __name: AA  <--- 監控 super.__init__ 的設定
  - @AA: __setattr__: name: _A__name
  - @AA: __setattr__: value: A
  - @AA: __setattr__: name: _A__age
  - @AA: __setattr__: value: 18
< @AA: __name: A  <--- 被改掉了, base 在設定 
< @AA: cannot access self.__age: <--- 可以讀取 base 設定的私有屬性 __age !!!
'A' object has no attribute '_A__real_age'
< @AA: cannot access self.__real_age:

若將 A2 命名成 B (檔案名稱仍為 a.py)

執行 main.py 的結果:

$ python3 main.py 

  - @AA: __setattr__: name: _B__name
  - @AA: __setattr__: value: AA
> @AA: __name: AA
  - @AA: __setattr__: name: _A__name
  - @AA: __setattr__: value: A
  - @AA: __setattr__: name: _A__age
  - @AA: __setattr__: value: 18
< @AA: __name: AA <--- 名子沒有被 base 變更
'B' object has no attribute '_B__age' <--- 無法讀取 base 設定的屬性值
< @AA: cannot access self.__age:
'B' object has no attribute '_B__real_age'
< @AA: cannot access self.__real_age:

測試結論:

  • 若有同名類別,且具有繼承關係,但位於不同模組下,他們會視為一體也就是,父類別 A1 的屬性設定,會直接 apply 到 A2 子類別
  • 即使中間有多一層子類別 B (如 A1 :: B :: A2),結果仍是一樣
  • 私有方法 (.__a_method(self)) 也如同屬性,在父類別 A1 呼叫私有方法,也會轉呼叫 A2 子類別同名的私有方法

預防措施:

  • 對「類別名稱」加入 namespace 當作前綴,盡量避免有類別同名情形
  • 對「屬性名稱」加入 namespace 當作前綴,盡量避免有屬性同名情形