Python的偽私有屬性


什么是偽私有屬性?

 

在Python中,沒有類似 private 之類的關鍵字來聲明私有方法或屬性。

Python中要聲明私有屬性,需要在屬性前加上雙下划線(但是結尾處不能有雙下划線),如:self.__a。然而這樣的什么方式並不是真正私有,而是“偽私有”。

Python的偽私有屬性,實際是通過變量名壓縮(mangling)來實現變量名局部化。變量名壓縮的規則:在初始的變量名頭部加上一個下划線,再加上類的名稱,最后是初始變量名的名稱。

執行以下代碼來驗證:

class A(object):
    def __func(self):pass

if __name__ == '__main__':
    print(A.__dict__)

運行結果:

{'__weakref__': <attribute '__weakref__' of 'A' objects>, '__module__': '__main__', '__doc__': None, '_A__func': <function A.__func at 0x10cfa037
8>, '__dict__': <attribute '__dict__' of 'A' objects>}

我們通過類的__dict__屬性,將class A的所有屬性打印出來,從打印的結果可以發現:原先定義的偽私有屬性(方法):__func 在__dict__中並不存在,取而代之的是_A_func這個方法,方法__func的變量名被壓縮。

 

如此在外部調用class A的__func方法時,會提示無法找到。修改代碼進行測試:

class A(object):
    def __func(self):pass

if __name__ == '__main__':
    a = A()
    a.__func()

運行后出現異常,提示A沒有屬性__func,從而實現類似私有屬性的功能。

AttributeError: 'A' object has no attribute '__func'

 

之所以說它是“偽私有”,是因為在了解偽私有變量的變量名壓縮規則后,可以根據壓縮規則進行調用。

再次修改代碼進行驗證:

class A(object):
    def __func(self):print('Hello Python')

if __name__ == '__main__':
    a = A()
    a._A__func()

運行結果正常, 成功打印“Hello Python”字符串。

Hello Python

 所以,Python的類並不存在正在的私有屬性,通過雙下划線實現的偽私有屬性,本質上是對變量名進行壓縮,使之無法直接在外部調用。

 

為什么要使用偽私有屬性

使用偽私有屬性是為了避免在類樹中,多個類賦值相同的屬性引發沖突問題。

假設有兩個類,C1 和 C2,他們都有相同的屬性X。

class C1():
    def meth1(self):
        self.x = 'Hello World'
    def meth2(self):
        print(self.x)
c1 = C1() c1.meth1()
c1.meth2()
class C2():
    def meth3(self):
        self.x = 'Hello Python'
    def meth4(self):
        print(self.x)
c2 = C2()
c2.meth3()
c2.meth4()

類C1和C2在單獨調用時,輸出結果沒有問題,符合預期:調用meth2方法時,打印meth1的賦值結果;調用meth4方法時,打印meth3的賦值結果。

此時增加一個新的類C3,繼承自C1、C2(多重繼承):

class C1():
    def meth1(self):
        self.x = 'Hello World'
    def meth2(self):
        print(self.x)

class C2():
    def meth3(self):
        self.x = 'Hello Python'
    def meth4(self):
        print(self.x)

class C3(C1, C2):
    pass

c3 = C3()
c3.meth1()
c3.meth3()
c3.meth2()
c3.meth4()

從運行結果可以看出,每次 print(self.x)的內容,取決於 self.x 最后一次賦值的內容。

Hello Python
Hello Python

在示例代碼中,先調用 c3.meth1() 進行賦值,self.x的值為“Hello World”,再調用 c3.meth3() 進行賦值時,self.x的值被覆蓋,目前的值為“Hello Python”。

后續再調用c3.meth2()打印self.x的值時,實際上打印的是最后一次賦值結果,這在有些情況下跟類的設計初衷是相違背的:在C1中,meth2希望打印的是在meth1中賦值的內容:“Hello World”。

 

在使用偽私有屬性后可以解決變量名self.x相互覆蓋的問題(因為self.__x 被壓縮成了 self._C1__x 和 self._C2__x,變量名不同,不會互相覆蓋):

class C1():
    def meth1(self):
        self.__x = 'Hello World'
    def meth2(self):
        print(self.__x)

class C2():
    def meth3(self):
        self.__x = 'Hello Python'
    def meth4(self):
        print(self.__x)

class C3(C1, C2):
    pass

c3 = C3()
c3.meth1()
c3.meth3()
c3.meth2()
c3.meth4()

運行結果符合C1的設計初衷:調用meth2時應該打印出meth1的賦值結果:

Hello World
Hello Python

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM