[地雷] 同名類別,且具有繼承關係,但位於不同模組下 - 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 當作前綴,盡量避免有屬性同名情形