Python的 私有 共有 命名
我熱情地邀請大家猜測下面這段程式的輸出:
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()
如果您已經猜對了,那麼可以不看我這篇博文了。如果你沒有猜對或者心裡有所疑問,那我的這篇博文正是為您所準備的。
一切由為什麼會輸出“A.__private()”開始。但要講清楚為什麼,我們就有必要了解一下Python的命名機制。
據 Python manual,變數名(識別符號)是Python的一種原子元素。當變數名被繫結到一個物件的時候,變數名就指代這個物件,就像人類社會一樣,不是嗎?當變數名出現在程式碼塊中,那它就是本地變數;當變數名出現在模組中,它就是全域性變數。模組相信大家都有很好的理解,但程式碼塊可能讓人費解些。在這裡解釋一下:
程式碼塊就是可作為可執行單元的一段Python程式文字;模組、函式體和類定義都是程式碼塊。不僅如此,每一個互動指令碼命令也是一個程式碼塊;一個指令碼檔案也是一個程式碼塊;一個命令列指令碼也是一個程式碼塊。
接下來談談變數的可見性,我們引入一個範圍的概念。範圍就是變數名在程式碼塊的可見性。如果一個程式碼塊裡定義本地變數,那範圍就包括這個程式碼塊。如果變數定義在一個功能程式碼塊裡,那範圍就擴充套件到這個功能塊裡的任一程式碼塊,除非其中定義了同名的另一變數。但定義在類中的變數的範圍被限定在類程式碼塊,而不會擴充套件到方法程式碼塊中。
迷蹤
據上節的理論,我們可以把程式碼分為三個程式碼塊:類A的定義、類B的定義和變數b的定義。根據類定義,我們知道程式碼給類A定義了三個成員變數(Python的函式也是物件,所以成員方法稱為成員變數也行得通。);類B定義了兩個成員變數。這可以通過以下程式碼驗證:
>>> print '/n'.join(dir(A))
_A__private
__init__
public
>>> print '/n'.join(dir(B))
_A__private
_B__private
__init__
public
咦,為什麼類A有個名為_A__private的 Attribute 呢?而且__private消失了!這就要談談Python的私有變數軋壓了。
探究
懂Python的朋友都知道Python把以兩個或以上下劃線字元開頭且沒有以兩個或以上下劃線結尾的變數當作私有變數。私有變數會在程式碼生成之前被轉換為長格式(變為公有)。轉換機制是這樣的:在變數前端插入類名,再在前端加入一個下劃線字元。這就是所謂的私有變數軋壓(Private name mangling)。如類A裡的__private識別符號將被轉換為_A__private,這就是上一節出現_A__private和__private消失的原因了。
再講兩點題外話:
一是因為軋壓會使識別符號變長,當超過255的時候,Python會切斷,要注意因此引起的命名衝突。
二是當類名全部以下劃線命名的時候,Python就不再執行軋壓。如:
>>> class ____(object):
def __init__(self):
self.__method()
def __method(self):
print '____.__method()'
>>> print '/n'.join(dir(____))
__class__
__delattr__
__dict__
__doc__
__getattribute__
__hash__
__init__
__method # 沒被軋壓
__module__
__new__
__reduce__
__reduce_ex__
__repr__
__setattr__
__str__
__weakref__
>>> obj = ____()
____.__method()
>>> obj.__method() # 可以外部呼叫
____.__method()
現在我們回過頭來看看為什麼會輸出“A.__private()”吧!
真相
相信現在聰明的讀者已經猜到答案了吧?如果你還沒有想到,我給你個提示:真相跟C語言裡的巨集預處理差不多。
因為類A定義了一個私有成員函式(變數),所以在程式碼生成之前先執行私有變數軋壓(注意到上一節標紅的那行字沒有?)。軋壓之後,類A的程式碼就變成這樣了:
class A(object):
def __init__(self):
self._A__private() # 這行變了
self.public()
def _A__private(self): # 這行也變了
print 'A.__private()'
def public(self):
print 'A.public()'
是不是有點像C語言裡的巨集展開啊?
因為在類B定義的時候沒有覆蓋__init__方法,所以呼叫的仍然是A.__init__,即執行了self._A__private(),自然輸出“A.__private()”了。
下面的兩段程式碼可以增加說服力,增進理解:
>>> class C(A):
def __init__(self): # 重寫__init__,不再呼叫self._A__private
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() # 呼叫一個沒有定義的函式,Python會把它給我的 ^_^~
self.public()
def __private(self):
print 'A.__private()'
def public(self):
print 'A.public()'
>>>a = A()
A.__private()
A.public()
相關文章
- Python中的私有屬性與私有方法Python
- Python中私有變數和私有方法Python變數
- Python私有變數Python變數
- python私有方法的使用注意Python
- Python命名規範Python
- Python中訪問私有屬性和私有方法Python
- 搭建python私有倉庫Python
- 淺談Python中的私有變數Python變數
- python呼叫私有屬性的方法總結Python
- python中變數的命名及詳解Python變數
- python命名元組如何理解Python
- 你真的懂Python命名嗎?Python
- Python基礎 - 命名規範Python
- python變數命名規則Python變數
- C++繼承詳解:共有(public)繼承,私有(private)繼承,保護(protected)繼承C++繼承
- python私有函式、專有方法Python函式
- c語言模擬Python的命名引數C語言Python
- Python 的類的下劃線命名有什麼不同?Python
- python 批量重新命名資料夾下的影像Python
- Python私有變數如何定義?Python學習教程!Python變數
- 四種常用的命名規則:帕斯卡命名法、駝峰命名法、下劃線命名法、匈牙利命名法
- Python私有函式和公開函式Python函式
- Python 訪問和設定私有屬性Python
- 使用Python批量重新命名資料夾中的檔案Python
- 在python中什麼是私有變數域Python變數
- python呼叫hanlp進行命名實體識別PythonHanLP
- python-進階教程-對切片進行命名Python
- Python中帶下劃線_的變數和函式命名的用法Python變數函式
- 一分鐘帶你瞭解Python私有變數的用法!Python變數
- JavaScript 的私有成員JavaScript
- ⦁ 類的私有成員
- 用Docker搭建cnpm私有倉庫以及私有倉庫的使用DockerNPM
- jQuery的$命名衝突jQuery
- Java命名:可怕的DefaultAbstractHelperImplJava
- 看看fishGUI的模式命名GUI模式
- 未命名
- NPM 私有倉庫的搭建NPM
- CocoaPods私有庫的建立