1. 程式人生 > >python下劃線,私有變數

python下劃線,私有變數

  轉自:http://blog.sina.com.cn/s/blog_58649eb30100g4zo.html Python用下劃線作為變數字首和字尾指定特殊變數。 " 單下劃線" 開始的成員變數叫做保護變數,意思是隻有類物件和子類物件自己能訪問到這些變數; 不能用“from xxx import *”而匯入
" 雙下劃線" 開始的是私有成員,意思是 只有類物件自己能訪問,連子類物件也不能訪問到這個資料。
雙下劃線開頭和結尾的代表python裡 特殊方法專用的標識,如 __init__()代表類的建構函式。
核心風格:避免用下劃線作為變數名的開始。
因為下劃線對直譯器有特殊的意義,而且是內建識別符號所使用的符號,建議避免用下劃線作為變數名的開始。

觀察下面這段程式的輸出:

class
A(object): def __init__(self): self.__private() self.public() def __private(self): print 'A.__private()' def public(self): print 'A.public()' class B(A): def __private(self): print 'B.__private()' def public(self): print
'B.public()' b = B()
正確的答案是:
A.__private()
B.public()
為什麼會有上述輸出呢?
我們先看一個小測試:
class A(object):
    def __init__(self):
        self.__private()
        self.public()
    def __private(self):
        print 'A.__private()'
    def public(self):
        print 'A.public()
' class B(A): def __private(self): print 'B.__private()' def public(self): print 'B.public()' print dir(A)

 

執行結果:
[' _A__private', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'public']
可以看到,類A有個名為_A__private的 Attribute,而且__private消失了!這就要談談 Python的私有變數軋壓了。
如果我們輸入:
b.__private()
得到的將是:
AttributeError: 'B' object has no attribute '__private'
也就是說 私有變數__foo經過mangle成為_ClassName__foo後繼承給了子類
Python把 以兩個下劃線開頭且沒有以兩個下劃線結尾的變數當作私有變數。
私有變數會在程式碼生成之前被轉換為長格式(變為公有)。
轉換機制是這樣的:在變數前端插入類名,再在前端加入一個下劃線字元。這就是所謂的 私有變數軋壓(Private name mangling)
如類A裡的__private識別符號將被轉換為_A__private,這就是前面的小測試出現_A__private,而__private消失的原因了。
注意下面2點:
一是因為軋壓會使識別符號變長,當超過255的時候,Python會切斷,要注意因此引起的命名衝突。
二是 當類名全部以下劃線命名的時候,Python就不再執行軋壓。如:
class ____(object):
    def __init__(self):
        self.__method()
    def __method(self):
        print '____.__method()'
 
print '\n'.join(dir(____))
instance=____()
instance.__method()#外界可以呼叫私有方法(因為沒有被軋壓)
執行結果:
__class__
__delattr__
__dict__
__doc__
__format__
__getattribute__
__hash__
__init__
__method  #沒有被軋壓
__module__
__new__
__reduce__
__reduce_ex__
__repr__
__setattr__
__sizeof__
__str__
__subclasshook__
__weakref__
____.__method()
____.__method()
現在我們回過頭來看看文章開頭為什麼會輸出“A.__private()”吧!這跟C語言裡的巨集預處理差不多。
因為類A定義了一個私有成員函式(變數),所以在程式碼生成之前先執行私有變數軋壓。軋壓之後,類A的程式碼就變成這樣了:
class A(object):
       def __init__(self):
              self.<span style="color:#ff0000;">_A__private()</span>        # 這行變了
              self.public()
       def <span style="color:#ff0000;">_A__private</span>(self):           # 這行也變了
              print 'A.__private()'
       def public(self):
              print 'A.public()'
是不是有點像C語言裡的巨集展開啊?
因為在類B定義的時候沒有覆蓋__init__方法,所以呼叫的仍然是A.__init__,即執行了self._A__private(),自然輸出“A.__private()”了。
下面我們再看一個例子:
class A():
    def __init__(self):
        self.__private()
        self.public()
    def __private(self):
        print 'A.__private()'
    def public(self):
        print 'A.public()'
class C(A):
    def __init__(self): # 重寫__init__
        self.__private() #這裡繫結的是_C_private
        self.public()
    def __private(self):
        print 'C.__private()'
    def public(self):
        print 'C.public()'
 
c=C()
執行結果:
C.__private()
C.public()
最後一個例子:
class A(object):  
    def __init__(self):  
        self._A__private()#貌似在呼叫一個未定義的函式,別擔心,軋壓後該函式會出現的。  
        self.public()  
    def __private(self):#該方法會被軋壓成_A__private  
        print 'A.__private()'  
    def public(self):  
        print 'A.public()'  
 
print dir(A)#可以看到,軋壓後,_A__private 方法出現了!
a=A()  

 

 

執行結果:
[' _A__private', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'public']
A.__private()
A.public()